Railway Operation Simulator  v2.9.2
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
63  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
64  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
65  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
66  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
67  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
68 /*
69  Construct a new train with general default values and input values for position and headcode.
70  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
71  This is because trains are kept in a vector and vectors erase elements during internal operations.
72  Deletion is explicit by using a special function. Increment the static class member NextTrainID
73  after setting this train's ID.
74 */
75 
76 {
77  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
78  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
79  AnsiString(TrainModeIn));
80  // AutoControl = true;//all trains start in auto control
81  UpdateCounter = 0;
82  TimeTimeLocArrived = false;
83  Derailed = false;
84  DerailPending = false;
85  Crashed = false;
86  StoppedAtBuffers = false;
87  StoppedAtSignal = false;
88  StoppedAtLocation = false;
89  StoppedAfterSPAD = false;
90  StoppedWithoutPower = false; // new at v2.4.0
91  StoppedForTrainInFront = false;
92  SignallerStoppingFlag = false;
93  SignallerStopped = false;
94  SignallerRemoved = false;
95  NotInService = false;
96  HoldAtLocationInTTMode = false;
97  AllowedToPassRedSignal = false;
98  CallingOnFlag = false;
99  BeingCalledOn = false;
100  DepartureTimeSet = false;
102  TimetableFinished = false;
103  LastActionDelayFlag = false;
104  OneLengthAccelDecel = false;
105  TrainCrashedInto = -1;
107  Plotted = false;
108  TrainGone = false;
109  SPADFlag = false;
110  FrontCodePtr = new Graphics::TBitmap;
111  FrontCodePtr->PixelFormat = pf8bit;
112  FrontCodePtr->Height = 8;
113  FrontCodePtr->Width = 8;
115  FrontCodePtr->Transparent = false;
116  AValue = sqrt(2 * PowerAtRail / Mass);
118  TerminatedMessageSent = false;
119  JoinedOtherTrainFlag = false;
121  StepForwardFlag = false;
123  for(int x = 0; x < 4; x++)
124  {
125  HeadCodeGrPtr[x] = new Graphics::TBitmap;
126  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
127  HeadCodeGrPtr[x]->Height = 8;
128  HeadCodeGrPtr[x]->Width = 8;
130  HeadCodeGrPtr[x]->Transparent = false;
131  }
132  for(int x = 0; x < 4; x++)
133  {
134  BackgroundPtr[x] = new Graphics::TBitmap;
135  BackgroundPtr[x]->PixelFormat = pf8bit;
136  BackgroundPtr[x]->Height = 8;
137  BackgroundPtr[x]->Width = 8;
139  BackgroundPtr[x]->Transparent = false;
140  }
141  for(int x = 0; x < 4; x++)
142  {
144  // set here to ensure have values
145  }
146  for(int x = 0; x < 4; x++)
147  {
148  PlotElement[x] = -1; // marker for not plotted yet
149  }
150  for(int x = 0; x < 3; x++)
151  {
152  OldZoomOutElement[x] = -1; // marker for not plotted yet
153  }
155  NextTrainID++;
156 
157  // new values added to complete initialisation of all TTrain variables
158 
159  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
160  // TrainDataEntryPtr, initialise in AddTrain
162  FrontElementLength = 0;
163  EntrySpeed = 0;
164  ExitSpeedHalf = 0;
165  ExitSpeedFull = 0;
166  MaxExitSpeed = 0;
167  BrakeRate = 0;
169  FirstHalfMove = true;
170  EntryTime = 0;
171  ExitTimeHalf = 0;
172  ExitTimeFull = 0;
173  ReleaseTime = 0;
174  TRSTime = 0;
175  LastActionTime = 0;
176  Straddle = MidLag;
177  LeadElement = -1;
178  LeadEntryPos = 0;
179  LeadExitPos = 0;
180  MidElement = -1;
181  MidEntryPos = 0;
182  MidExitPos = 0;
183  LagElement = -1;
184  LagEntryPos = 0;
185  LagExitPos = 0;
186  TrainFailed = false; // added at v2.4.0
187  for(int x = 0; x < 4; x++)
188  {
189  HOffset[x] = 0;
190  VOffset[x] = 0;
191  PlotEntryPos[x] = 0;
192  }
193  OpTimeToAct = 60; // default value, new at v2.2.0
194  MinsDelayed = 0.0; // new at v2.2.0
195  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
196  FinishJoinLogSent = false;
197  // added at v2.4.0 to prevent repeatdly logging the event
200  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
204  ZeroPowerNoCDTMessage = false;
209  TrainFailurePending = false;
210  Utilities->CallLogPop(648);
211 }
212 
213 // ---------------------------------------------------------------------------
214 
215 void TTrain::DeleteTrain(int Caller)
216 /*
217  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
218  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
219  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
220  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
221  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
222  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
223  No need to delete HeadCodePosition as that just points to existing bitmaps
224 */{
225  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
226  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
227  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
228  if(Display->ZoomOutFlag)
229  {
231  }
232  if(FrontCodePtr == 0)
233  {
234  throw Exception("Error in attempting to delete FrontCodePtr");
235  }
236  delete FrontCodePtr;
237  FrontCodePtr = 0;
238  for(int x = 0; x < 4; x++)
239  {
240  if(BackgroundPtr[x] == 0)
241  {
242  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
243  }
244  delete BackgroundPtr[x];
245  BackgroundPtr[x] = 0;
246  }
247  for(int x = 0; x < 4; x++)
248  {
249  if(HeadCodeGrPtr[x] == 0)
250  {
251  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
252  }
253  delete HeadCodeGrPtr[x];
254  HeadCodeGrPtr[x] = 0;
255  }
256  Utilities->CallLogPop(649);
257 }
258 
259 // ---------------------------------------------------------------------------
260 
262 /*
263  Plots the train starting position on screen. Note that the check for starting on straight points &
264  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
265  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
266  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
267  Set the headcode graphics pointers from the headcode text, then check whether starting at a
268  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
269  for the continuation element. Otherwise set Lead and Mid values,
270 
271  and Lead element value unless
272  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
273  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
274  then check if a train on either Mid or Lag and if so give a warning message and return false so
275  that the calling function can delete the train. Plot the Mid element train values then do similarly
276  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
277  the train. Finally set the Plotted flag and return true.
278 */{
279  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
280  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
281 
283  // PlotStartTime = TrainController->TTClockTime;
284  FirstHalfMove = true;
285 
286  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
287  // 'claim' it for this train to prevent any other waiting trains trying to enter
289  {
290  LagElement = -1; // not to be plotted
291  LagExitPos = 0; // not to be plotted
292  LagEntryPos = 0; // not to be plotted
293  MidElement = -1; // not to be plotted
294  MidExitPos = 0; // not to be plotted
295  MidEntryPos = 0; // not to be plotted
297  LeadExitPos = 1; // will be 1 for continuation entry
298  LeadEntryPos = 0;
299 
301  MaxExitSpeed = StartSpeed; // initial value
303  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
304  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
305  if(EntrySpeed > SpeedLimit)
306  {
307  EntrySpeed = SpeedLimit;
308  }
310  {
312  }
314  // LeadElement is the element to be entered
315 
316  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
317  // can achieve ExitSpeedFull at the half braking rate.
319  {
320  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
321  if(TempEntrySpeed < EntrySpeed)
322  {
323  EntrySpeed = TempEntrySpeed;
325  }
326  }
327  Straddle = MidLag; // only for starting on a continuation
329  // no need to stop gap flashing if start on continuation
330  }
331  else // not starting at a continuation
332  {
333  LagElement = -1;
334  LagEntryPos = 0;
335  LagExitPos = 0;
342 
344  MaxExitSpeed = StartSpeed; // initial value
346  bool TempDerail = false; // dummy
347  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
349  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
350  {
351  StoppedWithoutPower = true;
352  }
353  // facing buffers check - ignore starting speed if start facing buffers
354  StoppedAtBuffers = false;
355  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
358  {
359  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
360  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
361  EntrySpeed = 0;
362  ExitSpeedHalf = 0;
363  ExitSpeedFull = 0;
364  MaxExitSpeed = 0;
365  // SetTrainMovementValues not called so set this here
366  BrakeRate = 0;
369  StoppedAtSignal = false;
370  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
371  // signal check is an 'else'
372  if(!StoppedAtLocation)
373  {
374  StoppedAtBuffers = true; // stopped at location takes precedence
375  }
376  }
377 
378  // facing continuation check - don't allow to stop even if no power
380  {
381  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
382  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
386  BrakeRate = 0;
387  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
388  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
389  }
390 
391  // Signal check
392  else if((NextElementPosition > -1) && (NextEntryPos > -1))
393  // condition check added as precaution after SloughIECC error reported by James U
394  {
395  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
396  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
397  {
398  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
399  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
400  EntrySpeed = 0;
401  ExitSpeedHalf = 0;
402  ExitSpeedFull = 0;
403  MaxExitSpeed = 0;
404  BrakeRate = 0;
407  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
408  {
409  StoppedAtSignal = true;
411  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
412  }
414  {
415  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
416  // forwards, but don't change the background colour so still shows as stopped at location
417  StoppedAtSignal = true;
418  }
419  }
420  else
421  {
422  StoppedAtSignal = false;
423  if(NextEntryPos > 1)
424  {
425  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
426  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
427  }
428  else
429  {
430  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
431  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
432  }
433  if(EntrySpeed > SpeedLimit)
434  {
435  EntrySpeed = SpeedLimit;
436  }
438  {
440  }
442  TDateTime TestTime = TrainController->TTClockTime; // test
443  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
444  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
445  // NextElement is the element to be entered
446 
447  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
448  // can achieve ExitSpeedFull at the half braking rate.
450  {
451  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
452  // half braking
453  if(TempEntrySpeed < EntrySpeed)
454  {
455  EntrySpeed = TempEntrySpeed;
456  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
457  }
458  }
459  }
460  }
462  {
463  throw Exception("Error, LeadElement Exit Connection is NotSet");
464  }
465  }
466  if(MidElement > -1) // will be -1 if start on continuation
467  {
468  Straddle = LeadMid;
472  {
473  for(int x = 0; x < 4; x++)
474  {
475  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
476  }
477  }
478  else
479  {
480  for(int x = 0; x < 4; x++)
481  {
483  }
484  }
485  if(TrainMode == Timetable)
486  {
488  }
489  else
490  {
492  }
494  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
495 
498 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
499  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
500  {
501  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
502  Utilities->CallLogPop(651);
503  return false;
504  }
505 */
510  PlotTrainGraphic(8, 0, Display);
511  PlotTrainGraphic(9, 1, Display);
512 
515 
516  // pick up background bitmaps [2] & [3]
517 
520 
521  PlotElement[2] = MidElement;
523  PlotElement[3] = MidElement;
525  PlotTrainGraphic(10, 2, Display);
526  PlotTrainGraphic(11, 3, Display);
527  // Plotted = true; set in PlotTrainGraphic
528  }
529  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
530  Utilities->CallLogPop(652);
531 }
532 
533 // ---------------------------------------------------------------------------
534 void TTrain::UnplotTrain(int Caller)
535 {
536  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
537  if(!Plotted)
538  {
539  return;
540  }
541  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
542 
543  if(Straddle == MidLag)
544  {
545  if(MidElement > -1)
546  {
551  // to force plot of locked route marker, needed once only for the element
552  }
553  if(LagElement > -1)
554  {
559  // to force plot of locked route marker, needed once only for the element
560  }
561  }
562  else if(Straddle == LeadMidLag)
563  {
564  if(LeadElement > -1)
565  {
568  // to force plot of locked route marker, needed once only for the element
569  }
570  if(MidElement > -1)
571  {
576  // to force plot of locked route marker, needed once only for the element
577  }
578  if(LagElement > -1)
579  {
582  // to force plot of locked route marker, needed once only for the element
583  }
584  }
585  else if(Straddle == LeadMid)
586  {
587  if(LeadElement > -1)
588  {
593  // to force plot of locked route marker, needed once only for the element
594  }
595  if(MidElement > -1)
596  {
601  // to force plot of locked route marker, needed once only for the element
602  }
603  }
604  if(LeadElement > -1)
605  {
607  }
608  if(MidElement > -1)
609  {
611  }
612  if(LagElement > -1)
613  {
615  }
616  Plotted = false;
618  Display->Update();
619  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
620  // resurrected when Update() dropped from PlotOutput etc
621  Utilities->CallLogPop(653);
622 }
623 
624 // ----------------------------------------------------------------------------
625 
626 void TTrain::UpdateTrain(int Caller)
627 /*
628  Note: Some changes made since comments written
629 
630  Brief:
631  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
632  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
633  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
634  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
635  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
636  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
637  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
638  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
639  changed to MidLag within the function and all elements moved down one, old Mid becomes
640  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
641  incremented to reflect the position the train now occupies.
642 
643  Detail:
644  Set TrainFailurePending if all conditions met
645  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
646  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
647  and return.
648  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
649  If there's a LagElement (there will be but include check for good practice - next
650  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
651  train fully on offending point - Derail set and DerailPanding reset, train background
652  colour changed (note that BackgroundColour is a property of the train itself) then return.
653  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
654  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
655  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
656  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
657  if LeadElement is a fouled trailing point.
658  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
659  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
660  replotting the last background segment and checking whether the element is a bridge or crossover with the other
661  track in a route, in which case the route colour is replotted.
662  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
663  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
664  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
665  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
666  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
667  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
668  train can be deleted by the calling function, and the function returns.
669  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
670  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
671  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
672  basic red aspect.
673 
674  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
675  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
676  regardless of direction, and with the correct front code colour.
677 
678  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
679  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
680  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
681  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
682  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
683  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
684  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
685 
686  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
687  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
688  changed similarly. The function then returns.
689 
690  If Crashed is not set then Straddle is incremented and the function returns.
691 */
692 
693 {
694  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
695  UpdateCounter++;
696  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
697  if(UpdateCounter >= 100)
698  {
699  UpdateCounter = 0;
700  }
701  int RandRange = (TrainController->MTBFHours * 3600) / 53;
702 
703  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
704  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
705  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
706  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
707  // RandomFailureCounter value is fixed for a full cycle of train updates so this
708  // makes sure there's no bunching of failures as there is for a fixed comparison number
709  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
710  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
711  {
712  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
713  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
714  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
715  // don't fail if:
716  // (a) on a continuation (entering or leaving);
717  // (b) already failed;
718  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
719  // (d) train terminated;
720  // (e) crashed or derailed; or
721  // (f) under signaller control and stopped.
722  {
723  if((random(RandRange)) == 0)
724  // max value for RandRange is over 2x10^9
725  {
726  // here if failure due
727  TrainFailurePending = true;
728  // fail when PlotElements set to proper Lead, Mid & Elements
729  }
730  }
731  }
732 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
733  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
734  {
735  StoppedWithoutPower = true;
736  }
737 */
738  int LockedVectorNumber;
739  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
740  // default values - these needed for route checker below
741  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
742 
744  {
746  }
747  if(Crashed || Derailed)
748  {
750  {
751  PlotTrain(7, Display);
752  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
753  Display->Update();
754  }
755  OpTimeToAct = 0.0;
756  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
757  Utilities->CallLogPop(1017);
758  return; // no further action, user has to remove or work around
759  }
761  {
763  }
765  {
767  }
769  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
770  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
771  // to move & then stop again at the same station
772  {
773  TimeTimeLocArrived = false;
774  }
775  if(!Stopped() && !SPADFlag && !TrainFailed)
776  {
778  }
779  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
780  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
781 /* old version where force a stop at buffers regardless of speed
782  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
783  else StoppedAtBuffers = false;
784 */
785 
786  // new version where crash if run into buffers
787  if(!Crashed)
788  {
789  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
790  {
791  if(ExitSpeedFull > 1)
792  {
793  Crashed = true;
797  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
798  // no need for missed action logs - will be sent when train removed
799  StoppedAtBuffers = false;
800  }
802  // stopped at location & stopped without power take precedence
803  {
804  StoppedAtBuffers = true;
805  }
806  else
807  {
808  StoppedAtBuffers = false;
809  }
810  }
811  else
812  {
813  StoppedAtBuffers = false;
814  }
815  }
816  else
817  {
818  StoppedAtBuffers = false;
819  }
820  // if crashed don't want stopped at buffers set
821 
822  // also crash if run into a level crossing that is changing or has barriers up
823  if(!Crashed)
824  {
825  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
826  {
827  int H = Track->TrackElementAt(873, LeadElement).HLoc;
828  int V = Track->TrackElementAt(874, LeadElement).VLoc;
829  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
830  {
831  Crashed = true;
835  // no need for missed action logs - will be sent when train removed
836  }
837  }
838  }
840  {
842  }
843  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
844  if((TrainMode == Timetable) && StoppedAtLocation && (ActionVectorEntryPtr->Command != "")) //if Command == "" then either TimeLoc or TimeTimeLoc
845  {
846  HoldAtLocationInTTMode = true;
847  }
848  else if(TrainMode == Timetable)
849  {
850  HoldAtLocationInTTMode = false;
851  }
852  // in Signaller mode HoldAtLocationInTTMode not changed
853 
854  // check if departure pending & set times unless already set
855  if(TrainMode == Timetable)
856  {
858  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
859  {
860  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
861  {
863  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
864  {
865  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
866  }
867  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
868  DepartureTimeSet = true;
869  }
870  }
871  }
873  {
874  OpTimeToAct = CalcTimeToAct(0); // called after ReleaseTime set
875  // calculate every 1 sec (in real time, not timetable time) for all trains
876  }
877  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
878  if(TrainMode == Timetable)
879  {
880  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
881  {
882  RemainHereLogNotSent = true;
883  }
885  {
886  // ignore TimeLoc & TTLoc departures
887  // Action logs given in functions
889  LastActionTime + TDateTime(30.0 / 86400)))
890  {
891  if(ActionVectorEntryPtr->Command == "fsp")
892  {
893  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
894  FrontTrainSplit(0);
895  if(TrainFailurePending) // ok, stopped so PlotElements set
896  {
897  TrainHasFailed(0);
898  }
899  Utilities->CallLogPop(2041);
900  return;
901  }
902  else if(ActionVectorEntryPtr->Command == "rsp")
903  {
904  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
905  RearTrainSplit(0);
906  if(TrainFailurePending) // ok, stopped so PlotElements set
907  {
908  TrainHasFailed(1);
909  }
910  Utilities->CallLogPop(2042);
911  return;
912  }
913  else if(ActionVectorEntryPtr->Command == "Fjo")
914  {
915  FinishJoin(0);
916  }
917  else if(ActionVectorEntryPtr->Command == "jbo")
918  {
919  JoinedBy(0);
920  }
921  else if(ActionVectorEntryPtr->Command == "cdt")
922  {
924  }
925  else if(ActionVectorEntryPtr->Command == "Fns")
926  {
927  NewTrainService(0);
928  }
929  else if(ActionVectorEntryPtr->Command == "Frh")
930  {
931  RemainHere(0);
932  }
933  else if(ActionVectorEntryPtr->Command == "Fer")
934  {
935  TimetableFinished = true;
936  }
937  // other aspects of 'Fer' dealt with in TTrain::Update()
938  else if(ActionVectorEntryPtr->Command == "F-nshs")
939  {
941  }
942  else if(ActionVectorEntryPtr->Command == "Frh-sh")
943  {
945  }
946  else if(ActionVectorEntryPtr->Command == "Fns-sh")
947  {
949  }
950 /*
951  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
952  shuttle headcode (no train creation)
953  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
954  remain here
955  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
956  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
957 */
958  }
959  }
960  else
961  {
963  {
965  }
966  }
967  }
968  if(TrainMode == Timetable)
969  {
970  if(StoppedAtBuffers)
971  {
972  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
973  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
974  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
975  if(BufferLocation == "")
976  {
978  }
979  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
980  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
981  {
985  {
987  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
988  // Drop missed actions so user can still use sig mode to get back on track
990  }
991  if(TrainFailurePending) // ok, stopped so PlotElements set
992  {
994  TrainHasFailed(2);
995  }
996  Utilities->CallLogPop(1020);
997  return;
998  }
999  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !StoppedAtLocation && (TrainController->TTClockTime >
1000  ReleaseTime))
1001  {
1004  {
1007  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1008  // Drop missed actions so user can still use sig mode to get back on track
1010  }
1011  if(TrainFailurePending) // ok, stopped so PlotElements set
1012  {
1014  TrainHasFailed(3);
1015  }
1016  Utilities->CallLogPop(1397);
1017  return;
1018  }
1019  }
1020  else
1021  {
1023  }
1024  }
1025  else
1026  {
1028  }
1029  if(TrainMode == Timetable)
1030  {
1032  {
1034  }
1036  {
1038  }
1039  }
1040  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1041  // restart after stopped for train in front
1042  int NextElementPosition, NextEntryPos;
1043 
1044  if(LeadElement > -1) // if an exit continuation then not set
1045  {
1046  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1047  {
1049  }
1050  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1051  {
1052  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1053  {
1054  LeadExitPos = 1;
1055  }
1056  else
1057  {
1058  LeadExitPos = 3;
1059  }
1060  }
1061  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1062  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1063  }
1064  else
1065  {
1066  NextElementPosition = -1;
1067  NextEntryPos = -1;
1068  }
1069  if((NextElementPosition > -1) && (NextEntryPos > -1))
1070  // may be buffers or continuation so need this check
1071  {
1072 /*
1073  Check whether calling-on conditions met:-
1074  a) approaching train has stopped at a signal but not at a location;
1075  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1076  change of direction (cdt), remaining here (Frh), or under signaller control);
1077  c) at least 1 platform available for the approaching train;
1078  d) points (if any) set for direct route into platform;
1079  e) approaching train is to stop at station;
1080  f) no more facing signals between train and platform;
1081  g) [dropped g]
1082  h) train in front preventing route being set far enough to release stop signal;
1083  i) train in front not exiting at continuation;
1084  j) signal must be within 4km of the stop platform;
1085  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily]; and
1086  l) no existing route conflicts with the route into the platform.
1087  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1088  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1089 */
1090  if(TrainMode == Timetable)
1091  {
1092  if(CallingOnAllowed(0))
1093  {
1094  CallingOnFlag = true;
1095  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1096  }
1097  else
1098  {
1099  if(CallingOnFlag)
1100  {
1102  }
1103  CallingOnFlag = false;
1104  }
1105  }
1106  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !StoppedAtLocation)
1107  {
1108  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1109  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1110  // sets StoppedAtSignal again & train doesn't move
1111  StoppedAtSignal = false;
1112  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1113  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1114  // LeadMidLag and front of train was on LeadElement (after the current move)
1116  EntrySpeed = 0;
1118  FirstHalfMove = true;
1119  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1120  // NextElement is the element to be entered
1121  }
1123  {
1124  if(ClearToNextSignal(0))
1125  {
1126  StoppedForTrainInFront = false;
1127  BeingCalledOn = false;
1128  EntrySpeed = 0;
1130  FirstHalfMove = true;
1131  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1132  }
1133  else
1134  {
1135  if(TrainFailurePending) // ok, stopped so PlotElements set
1136  {
1137  TrainHasFailed(4);
1138  }
1139  Utilities->CallLogPop(1097);
1140  return;
1141  }
1142  }
1143  }
1144  if((Straddle == MidLag) && (LeadElement != -1))
1145  // later check only for Straddle == LeadMid, so need this check here for initial train start
1146  {
1148  }
1149 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1150  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1151  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1152  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1153  which could be when start as Snt.
1154  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1155  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1156  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1157  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1158  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1159  reached.
1160  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1161  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1162  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1163  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1164  sending a message to the performancelog.
1165 */
1166 
1167  if(TrainMode == Timetable)
1168  {
1170  {
1171  if(BeingCalledOn)
1172  {
1173  StoppedForTrainInFront = true;
1174  }
1176  {
1178  }
1180  {
1181  // value updated at every scheduled departure & arrival
1183  AnsiString StationName;
1185  {
1187  }
1189  {
1191  }
1192  else
1193  {
1194  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1195  }
1196  EntrySpeed = 0;
1200  FirstHalfMove = true;
1201  StoppedAtLocation = false;
1202 
1203  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1204  {
1205  StoppedWithoutPower = true;
1206  }
1207  if((NextElementPosition > -1) && (NextEntryPos > -1))
1208  // condition check added for SloughIECC error reported by James U
1209  {
1210  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1211  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1212  {
1213  StoppedAtSignal = true;
1214  if(!StoppedWithoutPower)
1215  // if stopped without power just keep existing background colour
1216  {
1218  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1219  }
1220  }
1221  }
1223  {
1224  TimeTimeLocArrived = false;
1225  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1226  // no warning for TimeTimeLoc departure
1227  }
1228  else
1229  {
1231  }
1232  DepartureTimeSet = false;
1233  // no need to set LastActionTime for a departure
1235  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1236  // note
1237 /*
1238  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1239  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1240  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1241  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1242  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1243  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1244  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1245 */
1247  {
1248  StoppedAtBuffers = true;
1249  }
1250  else if(!StoppedWithoutPower)
1251  // if buffers or no power, don't set values
1252  {
1254  {
1255  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1256  // NextElement is the element to be entered
1257  }
1258  else
1259  {
1261  // use LeadElement for an exit continuation
1262  }
1263  }
1264  }
1265  }
1266  }
1267  if(Straddle == LeadMidLag)
1268  {
1270  {
1271  Utilities->CallLogPop(654);
1272  return;
1273  }
1274  }
1275  else
1276  {
1278  {
1279  Utilities->CallLogPop(655);
1280  return;
1281  }
1282  }
1283  if((LeadElement > -1) && (MidElement > -1))
1284  {
1286  {
1287  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1288  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1289  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1290  SignallerStoppingFlag = false;
1291  StepForwardFlag = false;
1292  }
1293  }
1294  if(Stopped())
1295  // this is what prevents another movement if the train is stopped
1296  {
1297  if(TrainFailurePending) // ok, stopped so PlotElements set
1298  {
1299  TrainHasFailed(5);
1300  }
1301  BrakeRate = 0;
1302  Utilities->CallLogPop(656);
1303  return;
1304  }
1305  // here when ready for next move
1306 
1307  // check for train in front & if so stop at next access (when train fully on element next to train)
1308  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1309  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1310  // variable TrainInFrontInSignallerModeFlag
1311  {
1312  if(LeadElement > -1)
1313  {
1314  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1315  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1316  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1317  // true if another train on NextEntryPos track whether bridge or not
1318  {
1319  StoppedForTrainInFront = true;
1320  }
1321  else
1322  {
1323  StoppedForTrainInFront = false;
1324  }
1325  }
1326  }
1327  if((Straddle == LeadMid) && SPADFlag)
1328  // give message + plot background when ready to move half past the signal
1329  {
1330  if(NextElementPosition > -1)
1331  {
1332  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1333  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1334  {
1335  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1337  // if goes past 2 signals then give message twice
1339  }
1340  }
1341  }
1342  if(Straddle == LeadMidLag)
1343  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1344  {
1345  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1346  if(SPADFlag)
1347  {
1348  if(ExitSpeedFull == 0)
1349  {
1350  StoppedAfterSPAD = true;
1351  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1352  }
1353  }
1355  {
1356  if(ExitSpeedFull == 0)
1357  {
1358  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1359  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1360  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1361  // is sent at the right time and once only.
1362  SignallerStopped = true;
1363  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1364  StepForwardFlag = false;
1365  SignallerStoppingFlag = false;
1366  TTrackElement TE;
1367  AnsiString Loc = "";
1368  bool LocNamed = false;
1369  if(LeadElement > -1)
1370  {
1371  TE = Track->TrackElementAt(782, LeadElement);
1372  if(TE.ActiveTrackElementName != "")
1373  {
1374  Loc = TE.ActiveTrackElementName;
1375  LocNamed = true;
1376  }
1377  else
1378  {
1379  Loc = "track element " + TE.ElementID;
1380  }
1381  }
1382  if((MidElement > -1) && !LocNamed)
1383  {
1384  TE = Track->TrackElementAt(783, MidElement);
1385  if(TE.ActiveTrackElementName != "")
1386  {
1387  Loc = TE.ActiveTrackElementName;
1388  LocNamed = true;
1389  }
1390  else if(Loc == "")
1391  {
1392  Loc = "track element " + TE.ElementID;
1393  }
1394  }
1395  if(Loc == "")
1396  {
1397  Loc = "outside railway";
1398  // must have stopped after left at a continuation (because both lead & mid == -1)
1399  }
1400  else
1401  {
1402  Loc = "at " + Loc;
1403  }
1404  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1405  }
1406  }
1407  if(LeadElement > -1) // if an exit continuation then not set
1408  {
1409  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1410  {
1412  }
1413  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1414  {
1415  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1416  {
1417  LeadExitPos = 1;
1418  }
1419  else
1420  {
1421  LeadExitPos = 3;
1422  }
1423  }
1424  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1425  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1426  }
1427  else
1428  {
1429  NextElementPosition = -1;
1430  NextEntryPos = -1;
1431  }
1434  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1435 
1436  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1437  {
1438  StoppedWithoutPower = true;
1439  }
1440  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1441  // may be buffers or continuation. SPADFlag added at v2.1.0
1442  // so don't override the SPAD colour & don't set StoppedAtSignal
1443  {
1444  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1445  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !StoppedAtLocation)
1446  {
1447  StoppedAtSignal = true;
1448  if(!StoppedWithoutPower)
1449  // leave background as is if no power, but set StoppedAtSignal
1450  {
1452  }
1453  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1454  }
1455  }
1456  if(!Stopped())
1457  {
1458  if((NextElementPosition > -1) && (NextEntryPos > -1))
1459  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1460  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1461  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1462  // function for fuller explanation
1463  {
1464  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1465  // NextElement is the element to be entered
1466  }
1467  // follow the continuation exits:-
1468  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1469  {
1471  // Use LeadElement for calcs if lead is a continuation
1472  }
1473  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1474  {
1476  // Use MidElement for calcs if mid is a continuation
1477  }
1478  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1479  {
1481  // Use LagElement for calcs if lag is a continuation
1482  }
1483  }
1484  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1485  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1486  // Trains may not be in a route
1487  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1488  {
1489  // NB if LeadElement == -1 then the above test returns false
1490  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1491  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1492  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1493  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1494  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1495  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1496  FirstPair.second).GetELink() == TempELink))
1497  {
1498  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1499  }
1500  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1501  SecondPair.second).GetELink() == TempELink))
1502  {
1503  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1504  }
1505  }
1506  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1507  // Trains may not be in a route
1508  {
1509  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1510  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1511  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1512  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1513  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1514  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1515  FirstPair.second).GetELink() == TempELink))
1516  {
1517  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1518  }
1519  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1520  SecondPair.second).GetELink() == TempELink))
1521  {
1522  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1523  }
1524  }
1525  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1526  // Trains may not be in a route
1527  {
1528  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1529  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1530  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1531  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1532  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1533  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1534  FirstPair.second).GetELink() == TempELink))
1535  {
1536  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1537  }
1538  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1539  SecondPair.second).GetELink() == TempELink))
1540  {
1541  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1542  }
1543  AllRoutes->CheckMapAndRoutes(8); // test
1544  }
1545  if(LagElement > -1)
1546  // not entering at a continuation so can deal with train leaving the lag element
1547  {
1549  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1550  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1551  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1552 
1553  TPrefDirElement PrefDirElement;
1554  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1555  // as this is a 16x16 graphic
1557  {
1559  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1560  }
1561  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1562  {
1563  int RouteNumber;
1564  TrainGone = true;
1565  // flag to indicate train to be deleted - outside this function
1567  {
1568  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1569  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1570  // calc distance from & inc last signal to exit
1571  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1572  int NewLastElement = 0, NewLastExitPos = 0;
1573  // need above because can't change LastElement & LastExitPos until both new values obtained
1574  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1575  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1576  LastElement).TrackType != Points))
1577  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1578  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1579  // leave CumDistance as it was in these circumstances.
1580  {
1581  if(LastExitPos < 2)
1582  {
1583  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1584  }
1585  else
1586  {
1587  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1588  }
1589  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1590  if(NewLastElement == -1)
1591  // this will catch buffers or any other connection failure
1592  {
1593  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1594  }
1595  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1596  if(NewLastExitPos == -1)
1597  {
1598  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1599  }
1600  LastElement = NewLastElement;
1601  LastExitPos = NewLastExitPos;
1602  }
1603  // if at signal add this in too
1604  if(CumDistance < 1200)
1605  {
1606  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1607  }
1608  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1609  // else use 1200m - CumDistance
1610  int FirstDistance = 0;
1611  if(CumDistance >= 1200)
1612  {
1613  FirstDistance = 100;
1614  }
1615  else
1616  {
1617  FirstDistance = 1200 - CumDistance;
1618  }
1619  if(FirstDistance < 100)
1620  {
1621  FirstDistance = 100; // don't allow < 100
1622  }
1623  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1624  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1625  if(ExitSpeedFull > 20.0)
1626  {
1627  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1628  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1629  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1630  // 4320.0 = 3.6 * 1200, .0 to make it a double
1631  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1632  }
1633  else
1634  {
1635  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1636  ContinuationAutoSigEntry.SecondDelay = 120.0;
1637  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1638  }
1639  ContinuationAutoSigEntry.AccessNumber = 0;
1640  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1642  {
1644  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1645  VectorIT++)
1646  {
1647  if(VectorIT->RouteNumber == RouteNumber)
1648  {
1649  // another train has passed out of same route so erase earlier entry
1650  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1651  break;
1652  }
1653  }
1654  }
1655  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1656  }
1658  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1659  Display->Update();
1660  // need to keep this since Update() not called for PlotSmallOutput as too slow
1661  Utilities->CallLogPop(659);
1662  return;
1663  }
1664  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1665  if(LeadElement > -1)
1666  {
1668  // changed to lead so reset early
1669  {
1670  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1672  // don't plot if zoomed out
1673  if(!Display->ZoomOutFlag)
1674  {
1676  }
1677  // covers signal resetting in same direction
1678  }
1679  }
1681  {
1682  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1683  {
1684  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1685  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1686  TPrefDirElement PrefDirElement;
1687  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1689  {
1691  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1692  }
1694  LockedVectorNumber)))
1695  {
1697  }
1698  }
1699  }
1700  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1701  {
1702  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1704  // don't plot if zoomed out
1705  if(!Display->ZoomOutFlag)
1706  {
1708  }
1709  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1710  }
1712  {
1713  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1714  {
1715  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1716  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1717  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1718  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1719  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1720  int RouteNumber;
1722  // already know it's an autosigsroute, this is just to get the RouteNumber
1723  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1724  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1725  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1726  int RouteNumber2;
1728  // already know it's an autosigsroute, this is just to get the RouteNumber
1729  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1730  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1731  {
1732  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1733  // this was in the 1.3.0 addition but without the condition
1734  }
1735  // end of 1.3.2 addition
1736  // end of 1.3.0.addition
1737  }
1738  TPrefDirElement PrefDirElement;
1739  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1741  {
1743  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1744  }
1745  }
1746  }
1747  }
1748  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1749  if(Straddle == LeadMid)
1750  {
1751  AllowedToPassRedSignal = false;
1752  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1753  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1754  if(DerailPending)
1755  // set during last GetLeadElement, but only act on it when train fully on offending point
1756  // i.e. next time Straddle reaches LeadMid
1757  {
1758  Derailed = true;
1759  DerailPending = false;
1763  Utilities->CallLogPop(657);
1764  return;
1765  }
1772  Straddle = MidLag;
1773  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1774  // LeadElement during this function (note that if stopped at signal then won't get this far)
1775  if(LeadElement > -1)
1776  {
1778  // i.e an exit continuation only
1779  // if don't exclude entry continuations then can't progress past it
1780  {
1781  LeadElement = -1;
1782  }
1783  else
1784  {
1785  GetLeadElement(0);
1786  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1788  if(Stopped())
1789  {
1790  if(TrainFailurePending) // ok, stopped so PlotElements set
1791  {
1792  TrainHasFailed(6);
1793  }
1794  Utilities->CallLogPop(658);
1795  return; // i.e. don't move forward one step if next element is a red signal
1796  }
1797  }
1798  }
1799  }
1800  if(LagElement > -1)
1801  {
1802  // below are the actions required at both half moves for LagElement > -1
1804 
1805  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1806  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1807  // need to do this for each half element
1808 
1809  TPrefDirElement PrefDirElement;
1810  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1811  {
1812  int RouteNumber; // holder for call below - not used
1814  {
1815  if(Utilities->clTransparent == TColor(0xFFFFFF))
1816  // change to black for a white background
1817  {
1819  // only applies for AutoSigs Route in case was locked & timed out
1820  }
1821  else
1822  // change to white for a dark background
1823  {
1825  // only applies for AutoSigs Route in case was locked & timed out
1826  }
1828  }
1829  }
1831  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1832  // or a train on the opposite track - needs to be replotted
1833  }
1834  // update all array values
1835  HOffset[3] = HOffset[2];
1836  HOffset[2] = HOffset[1];
1837  HOffset[1] = HOffset[0];
1838  VOffset[3] = VOffset[2];
1839  VOffset[2] = VOffset[1];
1840  VOffset[1] = VOffset[0];
1841  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1842 
1843  BackgroundPtr[3] = BackgroundPtr[2];
1844  BackgroundPtr[2] = BackgroundPtr[1];
1845  BackgroundPtr[1] = BackgroundPtr[0];
1846  BackgroundPtr[0] = TempPtr;
1847 
1848  // update headcode graphics depending on Lead entry value
1849  if(LeadElement > -1) // if Lead is -1 then stays as is
1850  {
1852  {
1853  for(int x = 0; x < 4; x++)
1854  {
1855  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1856  }
1857  }
1858  else
1859  {
1860  for(int x = 0; x < 4; x++)
1861  {
1863  }
1864  }
1865  }
1866  if(TrainMode == Timetable)
1867  {
1869  }
1870  else
1871  {
1873  }
1875 
1876  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1877  if(LeadElement > -1)
1878  {
1879  if(Straddle == MidLag)
1880  // just about to move half onto the new lead element
1881  {
1883  // pick up new background bitmap [0]
1885  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1886  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1887  // check if own ID for entry at continuation, else crashes into itself!
1888  {
1889  // OK if crossing on a bridge
1890  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1891  if(OtherTrainEntryPos == -1)
1892  {
1893  throw Exception("Error - OtherTrainEntryPos not set");
1894  }
1895  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1896  // LeadEntryPos for rear end crashes
1897  (LeadExitPos == OtherTrainEntryPos))
1898  // LeadExitPos for head-on crashes
1899  {
1901  Crashed = true; // only set if Straddle = MidLag
1902  CallingOnFlag = false;
1903  // in case was set, need to disable call on if call on button had been pressed
1904  }
1905  }
1906  else if(MidElement > -1) // will be -1 for continuation entries
1907  {
1908  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1909  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1910  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1911  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1912  int OtherTrainID = -1;
1913  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1914  {
1915  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1916  {
1917  TrainCrashedInto = OtherTrainID;
1918  Crashed = true; // only set if Straddle = MidLag
1919  CallingOnFlag = false;
1920  // in case was set, need to disable call on if call on button had been pressed
1921  }
1922  }
1923  }
1924  }
1925  else
1926  {
1928  // pick up new background bitmap [0]
1930  }
1931  PlotElement[0] = LeadElement;
1933  PlotTrainGraphic(12, 0, Display);
1934  }
1935  if(MidElement > -1)
1936  {
1937  PlotElement[2] = MidElement;
1939  PlotTrainGraphic(1, 2, Display);
1940  }
1941  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
1942  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
1943  if(Straddle == MidLag)
1944  {
1945  if(MidElement > -1)
1946  {
1947  PlotElement[1] = MidElement;
1949  PlotTrainGraphic(2, 1, Display);
1950  }
1951  if(LagElement > -1)
1952  {
1953  PlotElement[3] = LagElement;
1955  PlotTrainGraphic(3, 3, Display);
1956  }
1957  }
1958  else // Straddle == LeadMidLag
1959  {
1960  if(LeadElement > -1)
1961  {
1962  PlotElement[1] = LeadElement;
1964  PlotTrainGraphic(4, 1, Display);
1965  }
1966  if(MidElement > -1)
1967  {
1968  PlotElement[3] = MidElement;
1970  PlotTrainGraphic(5, 3, Display);
1971  }
1972  }
1973  if(Crashed)
1974  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
1975  {
1980  // in case was set, need to disable call on if call on button had been pressed
1987  Straddle = LeadMidLag;
1988  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
1989  Display->Update();
1990  // resurrected when Update() dropped from PlotOutput etc
1991  Utilities->CallLogPop(660);
1992  return;
1993  }
1994  // deal here with station stops & pass times after all replotting done but before Straddle changed
1995  if(TrainMode == Timetable)
1996  {
1997  if(Straddle == LeadMidLag)
1998  {
1999  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2000  {
2001  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2002  // to point to the location arrival entry - before a change of direction
2003  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2004  bool StopRequired = false;
2005  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2006  if(TTVPos > -1) // -1 if can't find it or if name is ""
2007  {
2008  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2009  // or a station where next element contains a train or a stop signal, if so
2010  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2011  // to test the actual track the train is on since it can't be a platform
2012  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2013  TTrackElement NextTrackElement; // default for now
2014  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2015  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2016  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2017  int NextElementEntryPos = -1;
2018  int NextElementExitPos = -1;
2019  bool TrainOnNextElement = false;
2020  bool StopSignalAtNextElement = false;
2021  if(ForwardConnection)
2022  // if no forward connection can't derive anything from it without errors
2023  {
2024  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2025  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2026  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2027  // this is only for signals so no need to worry about points ambiguity
2028  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2029  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2030  }
2031  // logic here is: if(train@stoplinkpos1 || train@stoplinkpos2 || (forward connection && (train on next element || stop signal at next element)))
2032  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2033  {
2034  if(TTVPos > 0)
2035  {
2037  ActionVectorEntryPtr += TTVPos;
2038  }
2039  if(StopRequired)
2040  {
2041  StoppedAtLocation = true;
2042  StoppedAtSignal = false;
2043  // may have been set earlier at line 925 so need to reset as
2044  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2045  // in zoom out mode
2046  if(!TrainFailed)
2047  {
2049  // pale green
2050  }
2053  {
2054  TimeTimeLocArrived = true;
2055  // used in case of later signaller control, when need to know
2056  // whether had arrived or not, to avoid sending the arrival
2057  // message twice, see TInterface::TimetableControl1Click
2058  }
2059  }
2060  else
2061  {
2063  }
2065  {
2067  }
2068  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2070  }
2071  }
2072  }
2073  }
2074  }
2075  if(Straddle == MidLag)
2076  {
2077  Straddle = LeadMidLag;
2078  FirstHalfMove = false;
2079  }
2080  else if(Straddle == LeadMidLag)
2081  {
2082  Straddle = LeadMid;
2083  FirstHalfMove = true;
2084  }
2085  else if(Straddle == LeadMid)
2086  {
2087  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2088  }
2089  if(TrainFailurePending) // ok, moving but PlotElements set above
2090  {
2091  TrainHasFailed(7);
2092  }
2093  Display->Update();
2094  // need to keep this since Update() not called for PlotSmallOutput as too slow
2095  Utilities->CallLogPop(661);
2096 }
2097 
2098 // ----------------------------------------------------------------------------
2099 
2100 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2101 {
2102  switch(CodeChar)
2103  {
2104  case '0':
2105  return(RailGraphics->Code0);
2106 
2107  case '1':
2108  return(RailGraphics->Code1);
2109 
2110  case '2':
2111  return(RailGraphics->Code2);
2112 
2113  case '3':
2114  return(RailGraphics->Code3);
2115 
2116  case '4':
2117  return(RailGraphics->Code4);
2118 
2119  case '5':
2120  return(RailGraphics->Code5);
2121 
2122  case '6':
2123  return(RailGraphics->Code6);
2124 
2125  case '7':
2126  return(RailGraphics->Code7);
2127 
2128  case '8':
2129  return(RailGraphics->Code8);
2130 
2131  case '9':
2132  return(RailGraphics->Code9);
2133 
2134  case 'A':
2135  return(RailGraphics->CodeA);
2136 
2137  case 'B':
2138  return(RailGraphics->CodeB);
2139 
2140  case 'C':
2141  return(RailGraphics->CodeC);
2142 
2143  case 'D':
2144  return(RailGraphics->CodeD);
2145 
2146  case 'E':
2147  return(RailGraphics->CodeE);
2148 
2149  case 'F':
2150  return(RailGraphics->CodeF);
2151 
2152  case 'G':
2153  return(RailGraphics->CodeG);
2154 
2155  case 'H':
2156  return(RailGraphics->CodeH);
2157 
2158  case 'I':
2159  return(RailGraphics->CodeI);
2160 
2161  case 'J':
2162  return(RailGraphics->CodeJ);
2163 
2164  case 'K':
2165  return(RailGraphics->CodeK);
2166 
2167  case 'L':
2168  return(RailGraphics->CodeL);
2169 
2170  case 'M':
2171  return(RailGraphics->CodeM);
2172 
2173  case 'N':
2174  return(RailGraphics->CodeN);
2175 
2176  case 'O':
2177  return(RailGraphics->CodeO);
2178 
2179  case 'P':
2180  return(RailGraphics->CodeP);
2181 
2182  case 'Q':
2183  return(RailGraphics->CodeQ);
2184 
2185  case 'R':
2186  return(RailGraphics->CodeR);
2187 
2188  case 'S':
2189  return(RailGraphics->CodeS);
2190 
2191  case 'T':
2192  return(RailGraphics->CodeT);
2193 
2194  case 'U':
2195  return(RailGraphics->CodeU);
2196 
2197  case 'V':
2198  return(RailGraphics->CodeV);
2199 
2200  case 'W':
2201  return(RailGraphics->CodeW);
2202 
2203  case 'X':
2204  return(RailGraphics->CodeX);
2205 
2206  case 'Y':
2207  return(RailGraphics->CodeY);
2208 
2209  case 'Z':
2210  return(RailGraphics->CodeZ);
2211 
2212  case 'a':
2213  return(RailGraphics->Code_a);
2214 
2215  case 'b':
2216  return(RailGraphics->Code_b);
2217 
2218  case 'c':
2219  return(RailGraphics->Code_c);
2220 
2221  case 'd':
2222  return(RailGraphics->Code_d);
2223 
2224  case 'e':
2225  return(RailGraphics->Code_e);
2226 
2227  case 'f':
2228  return(RailGraphics->Code_f);
2229 
2230  case 'g':
2231  return(RailGraphics->Code_g);
2232 
2233  case 'h':
2234  return(RailGraphics->Code_h);
2235 
2236  case 'i':
2237  return(RailGraphics->Code_i);
2238 
2239  case 'j':
2240  return(RailGraphics->Code_j);
2241 
2242  case 'k':
2243  return(RailGraphics->Code_k);
2244 
2245  case 'l':
2246  return(RailGraphics->Code_l);
2247 
2248  case 'm':
2249  return(RailGraphics->Code_m);
2250 
2251  case 'n':
2252  return(RailGraphics->Code_n);
2253 
2254  case 'o':
2255  return(RailGraphics->Code_o);
2256 
2257  case 'p':
2258  return(RailGraphics->Code_p);
2259 
2260  case 'q':
2261  return(RailGraphics->Code_q);
2262 
2263  case 'r':
2264  return(RailGraphics->Code_r);
2265 
2266  case 's':
2267  return(RailGraphics->Code_s);
2268 
2269  case 't':
2270  return(RailGraphics->Code_t);
2271 
2272  case 'u':
2273  return(RailGraphics->Code_u);
2274 
2275  case 'v':
2276  return(RailGraphics->Code_v);
2277 
2278  case 'w':
2279  return(RailGraphics->Code_w);
2280 
2281  case 'x':
2282  return(RailGraphics->Code_x);
2283 
2284  case 'y':
2285  return(RailGraphics->Code_y);
2286 
2287  case 'z':
2288  return(RailGraphics->Code_z);
2289 
2290  default:
2291  return(RailGraphics->TempHeadCode);
2292  }
2293 }
2294 
2295 // ----------------------------------------------------------------------------
2296 
2297 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2298 {
2299  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2300  if(Code.Length() != 4)
2301  {
2302  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2303  }
2304  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2305  {
2306  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2307  }
2308  if(BackgroundColour != clB5G5R5)
2309  // i.e. not the basic graphic colour as loaded from resource file
2310  {
2311  for(int x = 0; x < 4; x++)
2312  {
2314  }
2315  }
2316  Utilities->CallLogPop(1484);
2317 }
2318 
2319 // ----------------------------------------------------------------------------
2320 
2321 void TTrain::GetLeadElement(int Caller)
2322 // assumes Mid & Lag already set, sets LeadElement,
2323 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2324 {
2325  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2326  DerailPending = false;
2330  {
2331  // attr 0=straight, - links 0 & 1 (0 = lead)
2332  // attr 1=diverging, - links 2 & 3 (2 = lead)
2333  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2334  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2335 
2336  // if enter at lead, exit at whatever attr set at
2337  // if enter at lag, exit at lead, but set derail wrt attribute
2338  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2339  {
2340  LeadExitPos = 1;
2341  }
2342 
2343  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2344  // best to be on safe side
2345  else if(LeadEntryPos == 0)
2346  {
2347  LeadEntryPos = 2;
2348  LeadExitPos = 3;
2349  }
2350  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2351  {
2352  LeadEntryPos = 0;
2353  LeadExitPos = 1;
2354  }
2355  else if(LeadEntryPos == 2)
2356  {
2357  LeadExitPos = 3;
2358  }
2359 
2360  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2361  {
2362  LeadExitPos = 0;
2363  }
2364  else if(LeadEntryPos == 1)
2365  {
2366  LeadExitPos = 0;
2367  DerailPending = true;
2368  }
2369  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2370  {
2371  LeadExitPos = 0;
2372  DerailPending = true;
2373  }
2374  else if(LeadEntryPos == 3)
2375  {
2376  LeadExitPos = 0;
2377  }
2378  }
2379  else if(LeadEntryPos == 0)
2380  {
2381  LeadExitPos = 1;
2382  }
2383  else if(LeadEntryPos == 1)
2384  {
2385  LeadExitPos = 0;
2386  }
2387  else if(LeadEntryPos == 2)
2388  {
2389  LeadExitPos = 3;
2390  }
2391  else if(LeadEntryPos == 3)
2392  {
2393  LeadExitPos = 2;
2394  }
2395  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2396 /* signal check moved to Update() function
2397  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2398  && (TrackElement.Attribute == 0))//0 = red
2399  {
2400  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2401  }
2402  else
2403  {
2404  StoppedAtSignal = false;
2405  }
2406 */
2407  Utilities->CallLogPop(662);
2408 }
2409 
2410 // ----------------------------------------------------------------------------
2411 
2412 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2413 {
2414  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2415  switch(Link)
2416  {
2417  case 1:
2418  {
2419  HOffset = 0;
2420  VOffset = 0;
2421  break;
2422  }
2423 
2424  case 2:
2425  {
2426  HOffset = 4;
2427  VOffset = 0;
2428  break;
2429  }
2430 
2431  case 3:
2432  {
2433  HOffset = 8;
2434  VOffset = 0;
2435  break;
2436  }
2437 
2438  case 4:
2439  {
2440  HOffset = 0;
2441  VOffset = 4;
2442  break;
2443  }
2444 
2445  case 6:
2446  {
2447  HOffset = 8;
2448  VOffset = 4;
2449  break;
2450  }
2451 
2452  case 7:
2453  {
2454  HOffset = 0;
2455  VOffset = 8;
2456  break;
2457  }
2458 
2459  case 8:
2460  {
2461  HOffset = 4;
2462  VOffset = 8;
2463  break;
2464  }
2465 
2466  case 9:
2467  {
2468  HOffset = 8;
2469  VOffset = 8;
2470  break;
2471  }
2472 
2473  default:
2474  {
2475  throw Exception("Error in GetOffsetValues - Link value wrong");
2476  }
2477  }
2478  Utilities->CallLogPop(674);
2479 }
2480 
2481 // ---------------------------------------------------------------------------
2482 
2483 bool TTrain::LowEntryValue(int EntryLink) const
2484 {
2485 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2486  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2487 */
2488  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2489  {
2490  return(true);
2491  }
2492  else
2493  {
2494  return(false);
2495  }
2496 }
2497 
2498 // ---------------------------------------------------------------------------
2499 
2500 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2501 {
2502  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2503  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2504  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2505  // default values
2506  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2507 
2508  TAllRoutes::TRouteType RouteType;
2509 
2510  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2511 
2512  TRect SourceRect, DestRect;
2513 
2514  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2515  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2516  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2517  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2518 
2519  TempGraphic->PixelFormat = pf8bit;
2520  TempGraphic->Width = 16;
2521  TempGraphic->Height = 16;
2522  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2523 
2524  if(TempElement.TrackType == Points)
2525  {
2526  TempGraphic->Assign(TempElement.GraphicPtr);
2527  TempGraphic->Transparent = true;
2528  TempGraphic->TransparentColor = Utilities->clTransparent;
2529  if(RouteType == TAllRoutes::AutoSigsRoute)
2530  {
2531  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2532  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2533  }
2534  else
2535  {
2536  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2537  }
2538  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2539  }
2540  else if(TempElement.TrackType == GapJump) // plot set gap
2541  {
2542  if(TempElement.SpeedTag == 88)
2543  {
2544  TempGraphic->Assign(RailGraphics->gl88set);
2545  }
2546  else if(TempElement.SpeedTag == 89)
2547  {
2548  TempGraphic->Assign(RailGraphics->gl89set);
2549  }
2550  else if(TempElement.SpeedTag == 90)
2551  {
2552  TempGraphic->Assign(RailGraphics->gl90set);
2553  }
2554  else if(TempElement.SpeedTag == 91)
2555  {
2556  TempGraphic->Assign(RailGraphics->gl91set);
2557  }
2558  else if(TempElement.SpeedTag == 92)
2559  {
2560  TempGraphic->Assign(RailGraphics->gl92set);
2561  }
2562  else if(TempElement.SpeedTag == 93)
2563  {
2564  TempGraphic->Assign(RailGraphics->bm93set);
2565  }
2566  else if(TempElement.SpeedTag == 94)
2567  {
2568  TempGraphic->Assign(RailGraphics->bm94set);
2569  }
2570  else if(TempElement.SpeedTag == 95)
2571  {
2572  TempGraphic->Assign(RailGraphics->gl95set);
2573  }
2574  TempGraphic->Transparent = true;
2575  TempGraphic->TransparentColor = Utilities->clTransparent;
2576  if(RouteType == TAllRoutes::AutoSigsRoute)
2577  {
2578  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2579  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2580  }
2581  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2582  }
2583  // new for version 0.6
2584  else if(TempElement.TrackType == SignalPost)
2585  {
2586  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2587  {
2588  for(int x = 0; x < 40; x++)
2589  {
2590  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2591  // need to stop aspect
2592  {
2593  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2594  break;
2595  }
2596  }
2597  }
2598  else // normal signal
2599  {
2600  TempGraphic->Assign(TempElement.GraphicPtr);
2601  // GraphicPtr set to normal signal in a signal track element
2602  }
2603  TempGraphic->Transparent = true;
2604  TempGraphic->TransparentColor = Utilities->clTransparent;
2605  if(RouteType == TAllRoutes::AutoSigsRoute)
2606  {
2607  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2608  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2609  }
2610  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2611  }
2612  else
2613  {
2614  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2615  // can't name points gaps or signals so 'else' OK
2616  bool FoundFlag;
2617  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2618  if(FoundFlag)
2619  {
2621  {
2622  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2623  TempGraphic->Assign(RailGraphics->bmName);
2624  TempGraphic->Transparent = true;
2625  TempGraphic->TransparentColor = Utilities->clTransparent;
2626  if(RouteType == TAllRoutes::AutoSigsRoute)
2627  {
2628  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2629  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2630  }
2631  else
2632  {
2633  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2634  }
2635  // draw track on top
2636  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2637  }
2638  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2639  {
2640  TempGraphic->Assign(TempElement.GraphicPtr);
2641  TempGraphic->Transparent = true;
2642  TempGraphic->TransparentColor = Utilities->clTransparent;
2643  // note that can't be an AutoSigsRoute
2644  // now overlay the LC central portion
2645  int BDVectorPos = -1; //not used
2646  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2647  {
2648  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2649  }
2650  else
2651  {
2652  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2653  }
2654  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2655  }
2656  else
2657  {
2658  TempGraphic->Assign(TempElement.GraphicPtr);
2659  TempGraphic->Transparent = true;
2660  TempGraphic->TransparentColor = Utilities->clTransparent;
2661  if(RouteType == TAllRoutes::AutoSigsRoute)
2662  {
2663  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2664  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2665  }
2666  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2667  }
2668  }
2669  else
2670  {
2671  TempGraphic->Assign(TempElement.GraphicPtr);
2672  TempGraphic->Transparent = true;
2673  TempGraphic->TransparentColor = Utilities->clTransparent;
2674  if(RouteType == TAllRoutes::AutoSigsRoute)
2675  {
2676  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2677  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2678  }
2679  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2680  }
2681  }
2682  delete TempGraphic;
2683  Utilities->CallLogPop(675);
2684 }
2685 
2686 // ---------------------------------------------------------------------------
2687 
2688 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2689 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2690 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2691 /*
2692 
2693  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2694  {
2695  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2696  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2697  TAllRoutes::TRouteType RouteType;
2698  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2699  // default values
2700  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2701  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2702  TRect SourceRect, DestRect, ScreenSourceRect;
2703  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2704 
2705  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2706  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2707  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2708 
2709  //add text & user graphics if any to *GraphicPtr prior to adding the track
2710  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2711  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2712  int Right = Left + 8;
2713  int Bottom = Top + 8;
2714  ScreenSourceRect.init(Left, Top, Right, Bottom);
2715  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2716  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2717 
2718  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2719  TempGraphic->PixelFormat = pf8bit;
2720  TempGraphic->Width = 16;
2721  TempGraphic->Height = 16;
2722 
2723  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2724  SourceGraphic->PixelFormat = pf8bit;
2725  SourceGraphic->Width = 16;
2726  SourceGraphic->Height = 16;
2727  SourceGraphic->Transparent = true;
2728  SourceGraphic->TransparentColor = Utilities->clTransparent;
2729 
2730  if (TempElement.TrackType == Points)
2731  {
2732  TempGraphic->Assign(TempElement.GraphicPtr);
2733  TempGraphic->Transparent = true;
2734  TempGraphic->TransparentColor = Utilities->clTransparent;
2735  if (RouteType == TAllRoutes::AutoSigsRoute)
2736  {
2737  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2738  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2739  }
2740  else
2741  {
2742  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2743  }
2744  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2745  }
2746  else if (TempElement.TrackType == GapJump) // plot set gap
2747  {
2748  if (TempElement.SpeedTag == 88)
2749  TempGraphic->Assign(RailGraphics->gl88set);
2750  else if (TempElement.SpeedTag == 89)
2751  TempGraphic->Assign(RailGraphics->gl89set);
2752  else if (TempElement.SpeedTag == 90)
2753  TempGraphic->Assign(RailGraphics->gl90set);
2754  else if (TempElement.SpeedTag == 91)
2755  TempGraphic->Assign(RailGraphics->gl91set);
2756  else if (TempElement.SpeedTag == 92)
2757  TempGraphic->Assign(RailGraphics->gl92set);
2758  else if (TempElement.SpeedTag == 93)
2759  TempGraphic->Assign(RailGraphics->bm93set);
2760  else if (TempElement.SpeedTag == 94)
2761  TempGraphic->Assign(RailGraphics->bm94set);
2762  else if (TempElement.SpeedTag == 95)
2763  TempGraphic->Assign(RailGraphics->gl95set);
2764  TempGraphic->Transparent = true;
2765  TempGraphic->TransparentColor = Utilities->clTransparent;
2766  if (RouteType == TAllRoutes::AutoSigsRoute) {
2767  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2768  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2769  }
2770  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2771  }
2772  // new for version 0.6
2773  else if (TempElement.TrackType == SignalPost)
2774  {
2775  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2776  {
2777  for (int x = 0; x < 40; x++)
2778  {
2779  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2780  // need to stop aspect
2781  {
2782  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2783  break;
2784  }
2785  }
2786  }
2787  else // normal signal
2788  {
2789  TempGraphic->Assign(TempElement.GraphicPtr);
2790  // GraphicPtr set to normal signal in a signal track element
2791  }
2792  TempGraphic->Transparent = true;
2793  TempGraphic->TransparentColor = Utilities->clTransparent;
2794  if (RouteType == TAllRoutes::AutoSigsRoute) {
2795  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2796  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2797  }
2798  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2799  }
2800  else {
2801  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2802  // can't name points gaps or signals so 'else' OK
2803  bool FoundFlag;
2804  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2805  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2806  if (FoundFlag)
2807  {
2808  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2809  {
2810  GraphicPtr->Canvas->CopyRect(DestRect,
2811  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2812  TempGraphic->Assign(RailGraphics->bmName);
2813  TempGraphic->Transparent = true;
2814  TempGraphic->TransparentColor = Utilities->clTransparent;
2815  if (RouteType == TAllRoutes::AutoSigsRoute)
2816  {
2817  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2818  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2819  }
2820  else
2821  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2822  // draw track on top
2823  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2824  SourceRect);
2825  }
2826  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2827  TempGraphic->Assign(TempElement.GraphicPtr);
2828  TempGraphic->Transparent = true;
2829  TempGraphic->TransparentColor = Utilities->clTransparent;
2830  // note that can't be an AutoSigsRoute
2831  // now overlay the LC central portion
2832  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2833  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2834  SourceRect);
2835  }
2836  else {
2837  TempGraphic->Assign(TempElement.GraphicPtr);
2838  TempGraphic->Transparent = true;
2839  TempGraphic->TransparentColor = Utilities->clTransparent;
2840  if (RouteType == TAllRoutes::AutoSigsRoute) {
2841  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2842  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2843  }
2844  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2845  SourceRect);
2846  }
2847  }
2848  else {
2849  TempGraphic->Assign(TempElement.GraphicPtr);
2850  TempGraphic->Transparent = true;
2851  TempGraphic->TransparentColor = Utilities->clTransparent;
2852  if (RouteType == TAllRoutes::AutoSigsRoute) {
2853  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2854  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2855  }
2856  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2857  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2858  }
2859  }
2860  delete TempGraphic;
2861  delete SourceGraphic;
2862  Utilities->CallLogPop();
2863  }
2864 */
2865 // ---------------------------------------------------------------------------
2866 
2867 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2868 {
2869  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2870  if(PlotElement[ArrayNumber] == -1)
2871  {
2872  Utilities->CallLogPop(676);
2873  return; // not plotted yet
2874  }
2875  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2876  // set before plot so gap flashing stops first
2877  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2878  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2879  // Only need to set ID for leading element, stays set until train finally leaves the element
2880  Plotted = true;
2881  Utilities->CallLogPop(677);
2882 }
2883 
2884 // ---------------------------------------------------------------------------
2885 
2886 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2887 {
2888  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2889  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2890 }
2891 
2892 // ---------------------------------------------------------------------------
2893 
2894 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2895 {
2896  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2897  HeadCode);
2898  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2899  {
2900  Utilities->CallLogPop(678);
2901  return(true);
2902  }
2903  else
2904  {
2905  Utilities->CallLogPop(679);
2906  return(false);
2907  }
2908 }
2909 
2910 // ---------------------------------------------------------------------------
2911 
2912 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2913 {
2914  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2915  "," + HeadCode);
2916  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2917  {
2918  Utilities->CallLogPop(680);
2919  return(true);
2920  }
2921  else
2922  {
2923  Utilities->CallLogPop(681);
2924  return(false);
2925  }
2926 }
2927 
2928 // ---------------------------------------------------------------------------
2929 
2930 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
2931 // test whether this train on a bridge on trackpos 0 & 1
2932 {
2933  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
2934  HeadCode);
2935  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
2936  {
2937  Utilities->CallLogPop(682);
2938  return(false);
2939  }
2940  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2941  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
2942  {
2943  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2944  {
2945  throw Exception("Error, same train on two different bridge tracks");
2946  }
2947  else
2948  {
2949  Utilities->CallLogPop(684);
2950  return(true);
2951  }
2952  }
2953  else
2954  {
2955  Utilities->CallLogPop(685);
2956  return(false);
2957  }
2958 }
2959 
2960 // ---------------------------------------------------------------------------
2961 
2962 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
2963 // test whether this train on a bridge on trackpos 2 & 3
2964 {
2965  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
2966  HeadCode);
2967  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
2968  {
2969  Utilities->CallLogPop(686);
2970  return(false);
2971  }
2972  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2973  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2974  {
2975  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
2976  Utilities->CallLogPop(687);
2977  return(true);
2978  }
2979  else
2980  {
2981  Utilities->CallLogPop(688);
2982  return(false);
2983  }
2984 }
2985 
2986 // ---------------------------------------------------------------------------
2987 
2988 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
2989 {
2990  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
2991  AnsiString(EntryPos) + "," + HeadCode);
2992  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
2993 
2994  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
2995  if(Track->GapFlashFlag)
2996  {
2998  {
3001  Track->GapFlashFlag = false;
3002  }
3003  }
3004  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3005  {
3006  if(EntryPos == -1)
3007  {
3008  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3009  }
3010  if(EntryPos < 2)
3011  {
3012  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
3013  }
3014  else
3015  {
3016  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
3017  }
3018  }
3019  Utilities->CallLogPop(690);
3020 }
3021 
3022 // ---------------------------------------------------------------------------
3023 
3024 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3025 {
3026  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3027  AnsiString(EntryPos) + "," + HeadCode);
3028  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3029  {
3030  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3031  }
3032  else
3033  {
3034  if(EntryPos == -1)
3035  {
3036  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3037  }
3038  if(EntryPos < 2)
3039  {
3040  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
3041  }
3042  else
3043  {
3044  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
3045  }
3046  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
3047  // i.e. other train on track 2&3
3048  {
3049  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
3050  }
3051  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
3052  // i.e. other train on track 1&2
3053  {
3054  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
3055  }
3056  else
3057  {
3058  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3059  }
3060  }
3061  Utilities->CallLogPop(691);
3062 }
3063 
3064 // ---------------------------------------------------------------------------
3065 
3066 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3067 {
3068  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3069  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3070  int LockedVectorNumber;
3071 
3072  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3073  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3074  {
3075  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3076  Utilities->CallLogPop(692);
3077  return;
3078  }
3079  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3080  // i.e other track is in a marked route
3081  // LinkPos doesn't have to be the entry position for the above check
3082  {
3083  TRect SourceRect, DestRect;
3084  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3085  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3086  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3087  // identify the route element for the other track
3088  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3089  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3090  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3091  int FirstELink, SecondELink = -1;
3092  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3093  // must be at least one
3094  if(RoutePair2.first > -1)
3095  {
3096  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3097  }
3098  TPrefDirElement RouteElement;
3099  // Graphics::TBitmap *RouteGraphic;
3100  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3101  // i.e. other track is in RoutePair2
3102  {
3103  if(SecondELink == -1)
3104  {
3105  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3106  }
3107  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3108  // error if both have same Link number
3109  {
3110  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3111  }
3112  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3113  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3114  }
3115  else // other track is in RoutePair1
3116  {
3117  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3118  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3119  }
3120  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3121  DestGraphic->PixelFormat = pf8bit;
3122  DestGraphic->Width = 8;
3123  DestGraphic->Height = 8;
3124  DestGraphic->Transparent = true;
3125  // has to be transparent or will overwrite the track that the train has just left
3126  DestGraphic->TransparentColor = Utilities->clTransparent;
3127  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3128  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3129  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3130  // plot locked route marker for other route if appropriate
3131  TPrefDirElement PrefDirElement; // holder for next call, unused
3132  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3133  if(StraddleValue == LeadMidLag)
3134  {
3136  PrefDirElement, LockedVectorNumber))
3137  {
3138  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3139  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3140  }
3141  }
3142  delete DestGraphic;
3143  }
3144  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3145  // also can only be a bridge or trains either have already or soon will crash
3146  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3147  {
3148  Utilities->CallLogPop(695);
3149  return;
3150  }
3151  if(ElementEntryPos > 1) // other train is on track 01
3152  {
3153  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3154  {
3156  }
3157  }
3158  else // other train is on track 23
3159  {
3160  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3161  {
3163  }
3164  }
3165  Utilities->CallLogPop(696);
3166 }
3167 
3168 // ---------------------------------------------------------------------------
3169 
3170 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3171 {
3172  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3173  AnsiString(EntryPos) + "," + HeadCode);
3174  int RouteNumber;
3175  bool WrongRoute = false;
3176  TPrefDirElement RouteElement;
3178  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3179 
3180  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3181  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3182  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3183  {
3184  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3185  {
3186  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3187  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3188  {
3189  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3190  {
3191  // don't call for stub end routes
3193  }
3194  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3195  Utilities->CallLogPop(697);
3196  return;
3197  }
3198  }
3199  // also need to check for a route on a crossing diagonal
3200  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3201  int LinkNumber = TrackElement.Link[EntryPos];
3202  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3203  {
3204  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3205  {
3206  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3207  bool LogActionErrorCalled = false;
3208  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3209  if(LinkNumber == 1)
3210  {
3211  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3212  {
3213  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3214  {
3215  // don't call for stub end routes
3217  LogActionErrorCalled = true;
3218  }
3219  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3220  }
3221  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3222  // not else in case have different routes on each diagonal, though shouldn't be possible
3223  {
3224  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3225  {
3226  // don't call for stub end routes
3228  }
3229  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3230  }
3231  }
3232 
3233  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3234  else if(LinkNumber == 3)
3235  {
3236  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3237  {
3238  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3239  {
3240  // don't call for stub end routes
3242  LogActionErrorCalled = true;
3243  }
3244  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3245  }
3246  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3247  // not else in case have different routes on each diagonal, though shouldn't be possible
3248  {
3249  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3250  {
3251  // don't call for stub end routes
3253  }
3254  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3255  }
3256  }
3257 
3258  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3259  else if(LinkNumber == 7)
3260  {
3261  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3262  {
3263  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3264  {
3265  // don't call for stub end routes
3267  LogActionErrorCalled = true;
3268  }
3269  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3270  }
3271  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3272  // not else in case have different routes on each diagonal, though shouldn't be possible
3273  {
3274  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3275  {
3276  // don't call for stub end routes
3278  }
3279  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3280  }
3281  }
3282 
3283  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3284  else if(LinkNumber == 9)
3285  {
3286  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3287  {
3288  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3289  {
3290  // don't call for stub end routes
3292  LogActionErrorCalled = true;
3293  }
3294  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3295  }
3296  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3297  // not else in case have different routes on each diagonal, though shouldn't be possible
3298  {
3299  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3300  {
3301  // don't call for stub end routes
3303  }
3304  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3305  }
3306  }
3307  }
3308  }
3309  Utilities->CallLogPop(698);
3310  return; // no route on other track or no other track
3311  }
3312  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3313  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3314  {
3315  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3316  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3317  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3318  {
3319  if(RouteElement.GetELinkPos() == EntryPos)
3320  {
3321  Utilities->CallLogPop(699);
3322  return; // right direction
3323  }
3324  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3325  {
3326  Utilities->CallLogPop(700);
3327  return; // right direction (points)
3328  }
3329  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3330  {
3331  Utilities->CallLogPop(701);
3332  return; // right direction (points)
3333  }
3334  else if(RouteElement.GetXLinkPos() == EntryPos)
3335  {
3336  WrongRoute = true;
3337  break; // wrong direction
3338  }
3339  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3340  {
3341  WrongRoute = true;
3342  break; // wrong direction
3343  }
3344  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3345  {
3346  WrongRoute = true;
3347  break; // wrong direction
3348  }
3349  }
3350  }
3351  if(!WrongRoute)
3352  {
3353  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3354  }
3355  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3356  {
3357  // don't call for stub end routes
3359  }
3360  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3361  Utilities->CallLogPop(703);
3362 }
3363 
3364 // ---------------------------------------------------------------------------
3365 
3366 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3367 {
3368  if(BackgroundColour == NewBackgroundColour)
3369  {
3370  return; // don't replot if already correct
3371 
3372  }
3373  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3374  bool ColourError = false, ColourError2 = false;
3375 
3376  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3377  if(ColourError)
3378  {
3379  ColourError2 = true;
3380  }
3381  for(int x = 0; x < 4; x++)
3382  {
3383  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3384  if(ColourError)
3385  {
3386  ColourError2 = true;
3387  }
3388  }
3389  if(ColourError2)
3390  {
3392  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3393  }
3394  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3395  // of motion
3396  for(int x = 0; x < 4; x++)
3397  {
3398  PlotTrainGraphic(6, x, Disp);
3399  }
3400  BackgroundColour = NewBackgroundColour;
3401  Display->Update();
3402  // need to keep this since Update() not called for PlotSmallOutput as too slow
3403  Utilities->CallLogPop(704);
3404 }
3405 
3406 // ---------------------------------------------------------------------------
3407 
3408 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3409 /*
3410 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3411 
3412 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3413 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3414 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3415 full-element moves.
3416 
3417 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3418 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3419 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3420 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3421 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3422 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3423 In this case set the brake rate to maximum to stop as soon as possible.
3424 
3425 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3426 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3427 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3428 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3429 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3430 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3431 
3432 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3433 first to see whether buffers or continuation) in turn is examined: first the length of the
3434 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3435 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3436 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3437 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3438 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3439 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3440 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3441 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3442 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3443 siding then again emeregency braking may be necessary and a crash may result.
3444 
3445 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3446 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3447 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3448 buffer, then the train accelerates for half the element and brakes for the other half.
3449 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3450 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3451 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3452 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3453 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3454 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3455 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3456 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3457 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3458 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3459 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3460 
3461 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3462 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3463 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3464 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3465 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3466 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3467 MaxBrakeRate.
3468 
3469 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3470 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3471 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3472 
3473 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3474 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3475 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3476 
3477 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3478 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3479 when Straddle == LeadMidLag
3480 */
3481 {
3482  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3483  AnsiString(EntryPos) + "," + HeadCode);
3484  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3485  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3486  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3487  TrainInFrontInSignallerModeFlag = false;
3488  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3489  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3490  bool SignallerStopRequired = false;
3491 
3493  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3494 
3495  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3496 
3497  OneLengthAccelDecel = false;
3498  BrakeRate = 0;
3499 
3500 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3501  if(CurrentTrackVectorPosition > -1)
3502  {
3503  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3504  {
3505  if((EntryPos == 0) || (EntryPos == 2))
3506  {
3507  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3508  {
3509  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3510  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3511  }
3512  else
3513  {
3514  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3515  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3516  }
3517  }
3518  else if(EntryPos == 1)
3519  {
3520  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3521  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3522  }
3523  else // == 3
3524  {
3525  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3526  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3527  }
3528  }
3529  else
3530  {
3531  if(EntryPos > 1)
3532  {
3533  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3534  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3535  }
3536  else
3537  {
3538  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3539  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3540  }
3541  }
3542  EntryHalfLength = CurrentElementHalfLength;
3543  FrontElementLength = 2 * CurrentElementHalfLength;
3544  }
3545  else
3546  {
3547  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3548  }
3549  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3550  {
3551  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3552  }
3553  // check if zero entry speed with another train directly in front & if so remain stopped
3554  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3555  {
3556  EntrySpeed = 0;
3557  ExitSpeedHalf = 0;
3558  ExitSpeedFull = 0;
3559  MaxExitSpeed = 0;
3560  BrakeRate = 0;
3561  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3562  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3563  StoppedForTrainInFront = true;
3564  Utilities->CallLogPop(705);
3565  return;
3566  }
3567  // new at v2.4.0 - check for stopped and zero power
3568  if((EntrySpeed < 1) && PowerAtRail < 1)
3569  {
3570  EntrySpeed = 0;
3571  ExitSpeedHalf = 0;
3572  ExitSpeedFull = 0;
3573  MaxExitSpeed = 0;
3574  BrakeRate = 0;
3575  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3576  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3577  StoppedWithoutPower = true;
3578  Utilities->CallLogPop(2125);
3579  return;
3580  }
3581 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3582  if(BeingCalledOn)
3583  {
3584  LimitingSpeed = CallOnMaxSpeed;
3585  }
3586  else
3587  {
3588  LimitingSpeed = MaximumSpeedLimit;
3589  }
3590  if(LimitingSpeed > FrontElementSpeedLimit)
3591  {
3592  LimitingSpeed = FrontElementSpeedLimit;
3593  }
3594  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3595  {
3596  LimitingSpeed = MaxRunningSpeed;
3597  }
3598  FrontElementMaxSpeed = LimitingSpeed;
3599 
3600 /*
3601  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3602  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3603  (2) V/3.6 = U/3.6 - FT;
3604  (3) S = UT/3.6 - 0.5FT^2
3605  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3606 
3607  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3608  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3609  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3610  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3611  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3612 
3613  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3614  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3615  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3616 */
3617 
3618 // check if running past a red signal without permission
3619  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3620  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3621  {
3622  SPADFlag = true; // user has to intervene to reset & restart after spad
3623  }
3624  if(!SPADFlag)
3625  {
3626  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3627 
3628  double ExitSpeedAtMaxBraking;
3629  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3630  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3632  {
3633  ExitSpeedAtMaxBraking = 0;
3634  }
3635  else
3636  {
3637  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3638  }
3639  double SpeedToUse;
3640  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3641  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3642  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3643  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3644  {
3645  SpeedToUse = ExitSpeedAtMaxBraking;
3646  }
3647  else
3648  {
3649  SpeedToUse = LimitingSpeed;
3650  }
3651  if(ExitSpeedFull > SpeedToUse)
3652  {
3653  ExitSpeedFull = SpeedToUse;
3654  }
3655  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3656  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3657 
3658  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3659  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3660  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3661 
3662  do
3663  {
3664  RedSignalFlag = false;
3665  BuffersFlag = false;
3666  StationFlag = false;
3667  BuffersOrContinuationNowFlag = false;
3668  ContinuationNextFlag = false;
3669  // have to reset this after the above test
3670  // add current element length to CumulativeLength
3671  CumulativeLength += (2 * CurrentElementHalfLength);
3672  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3673  {
3674  SignallerStopRequired = true;
3675  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3676  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3677  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3678  if(SignallerStopBrakeRate < TempBR)
3679  {
3680  SignallerStopBrakeRate = TempBR;
3681  }
3682  }
3683  // first check for stops within the length of the current element, where don't want any more checks & don't want
3684  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3685  // during the last loop when the NextTrackVectorPosition was the signal.
3686 
3687  // check if current element is a buffer
3688  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3689  {
3690  // no need to add in the length of this element to CumulativeLength as already included
3691  BuffersFlag = true;
3692  }
3693  // check if current element is a station stop
3694  if(TrainMode == Timetable)
3695  {
3696  bool StopRequired = false;
3697  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3698  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3699  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3700  {
3701  // no need to add in the length of element to CumulativeLength
3702  if(StopRequired)
3703  {
3704  StationFlag = true;
3705  }
3706  }
3707  }
3708  else
3709  {
3710  StationFlag = false;
3711  }
3712  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3713  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3714  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3715  {
3716  BuffersOrContinuationNowFlag = true;
3717  }
3718  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3719  {
3720  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3721  {
3722  if((EntryPos == 0) || (EntryPos == 2))
3723  {
3724  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3725  {
3726  ExitPos = 1;
3727  }
3728  else
3729  {
3730  ExitPos = 3;
3731  }
3732  }
3733  else
3734  {
3735  ExitPos = 0;
3736  }
3737  }
3738  else
3739  {
3740  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3741  }
3742  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3743  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3744  if(NextTrackVectorPosition > -1)
3745  {
3746  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3747  // this test & section added at v0.6
3748  {
3749  if((NextEntryPos == 0) || (NextEntryPos == 2))
3750  {
3751  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3752  {
3753  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3754  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3755  }
3756  else
3757  {
3758  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3759  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3760  }
3761  }
3762  else if(NextEntryPos == 1)
3763  {
3764  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3765  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3766  }
3767  else // == 3
3768  {
3769  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3770  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3771  }
3772  }
3773  else
3774  {
3775  if(NextEntryPos > 1)
3776  {
3777  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3778  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3779  }
3780  else
3781  {
3782  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3783  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3784  }
3785  }
3786  }
3787  else
3788  {
3789  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3790  }
3791  // now check for stops, first cover those where don't want to add in length of next element
3792  // check if next element is a red signal - Attr 0,
3793  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3794  // CurrentTrackVectorPosition not NextTrackVectorPosition
3795  bool StopRequired;
3796  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3797  {
3798  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3799  {
3800  // no need to add in the length of element to CumulativeLength
3801  RedSignalFlag = true;
3802  }
3803  // next element is a red signal
3804  }
3805  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3806  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3807  // at least one platform element free
3809  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3810  NextEntryPos, TrainID))
3811  {
3812  // no need to add in the length of element to CumulativeLength
3813  if(StopRequired)
3814  {
3815  StationFlag = true;
3816  }
3817  }
3818  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3819  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3820  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3821  {
3822  // no need to add in the length of element to CumulativeLength
3823  TrainInFrontInSignallerModeFlag = true;
3824  }
3825  // check if next element is a buffer
3826  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3827  {
3828  // need to add in the length of that element to CumulativeLength
3829  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3830  BuffersFlag = true;
3831  }
3832  // check if next element is a station stop
3834  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3835  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3836  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3837  {
3838  // need to add in the length of that element to CumulativeLength if a stop required
3839  if(StopRequired)
3840  {
3841  StationFlag = true;
3842  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3843  }
3844  }
3845  }
3846  //now can decide whether need to stop over CumulativeLength
3847  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3848  {
3849  // have to come to a stop over CumulativeLength
3850  if(CumulativeLength == FrontElementLength)
3851  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3852  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3853  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3854  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3855  // if not calc speed at halfway point & if less than above set half speed to this value;
3856  // use constant acceleration in calculating half time point
3857  {
3858  MaxExitSpeed = 0;
3859  double MaxHalfSpeed;
3860  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3861  // have to halve the element length, & can't be zero or negative so no need to test
3862  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3863  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3864  {
3865  MaxHalfSpeed = FrontElementMaxSpeed;
3866  }
3867  else
3868  {
3869  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3870  }
3871  if(MaxHalfSpeed > (2 * EntrySpeed))
3872  // use 2x to prevent kangarooing at last element when had
3873  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3874  {
3875  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3876  0.333334);
3877  bool HalfSpeedLimited = false;
3878  if(MaxHalfSpeed < ExitSpeedHalf)
3879  {
3880  ExitSpeedHalf = MaxHalfSpeed;
3881  HalfSpeedLimited = true;
3882  }
3883  if(PowerAtRail > 1)
3884  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3885  {
3886  // [km/h/3.6 = m/s]
3887  ExitTimeHalf =
3888  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3889  }
3890  else
3891  {
3892  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3893  }
3894  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3895  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3896  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3897  // by a long braking period
3898  ExitSpeedFull = 0;
3899  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3900  if(TempBrakeRate > MaxBrakeRate)
3901  {
3902  TempBrakeRate = MaxBrakeRate;
3903  }
3904  // shouldn't be but leave in anyway
3905  if(TempBrakeRate > BrakeRate)
3906  {
3907  BrakeRate = TempBrakeRate;
3908  }
3909  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3910  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3911  if(HalfSpeedLimited)
3912  // this is the change referred to above
3913  {
3914  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
3915  ExitTimeHalf = EntryTime + BrakingTime;
3916  ExitTimeFull = ExitTimeHalf + BrakingTime;
3917  }
3918  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
3919  Utilities->CallLogPop(1095);
3920  return;
3921  }
3922  }
3923  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
3924  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
3925  // calc th, tf, sh, & sf
3926  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3927  if(TempBrakeRate > MaxBrakeRate)
3928  {
3929  TempBrakeRate = MaxBrakeRate;
3930  }
3931  if(TempBrakeRate > BrakeRate)
3932  {
3933  BrakeRate = TempBrakeRate;
3934  }
3935  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3936  if(SignallerStopRequired)
3937  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
3938  {
3940  {
3942  // this prevents the brakerate from reducing for a signaller stop
3943  // regardless of other conditions that may change as progress round the loop
3944  }
3945  }
3947  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
3948  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
3949  {
3951  }
3952  int TempMaxExitSpeed;
3953  // calc current value & if less than MaxExitSpeed set that to this
3954  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
3955  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3956  {
3957  MaxExitSpeedAtHalfBraking = 0;
3958  }
3959  else
3960  {
3961  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3962  }
3963  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
3964  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
3965  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3966  {
3967  TempMaxExitSpeed = FrontElementMaxSpeed;
3968  }
3969  else
3970  {
3971  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
3972  }
3973  if(TempMaxExitSpeed < MaxExitSpeed)
3974  {
3975  MaxExitSpeed = TempMaxExitSpeed;
3976  }
3977  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
3978  // Cumulativelength, and Cumulativelength
3979 
3980  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
3981  {
3982  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
3983  if(ExitSpeedHalfSquared < 10)
3984  {
3985  ExitSpeedHalf = 0;
3986  }
3987  else
3988  {
3989  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
3990  }
3991  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
3992  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
3993  if(ExitSpeedFullSquared < 10)
3994  {
3995  ExitSpeedFull = 0;
3996  }
3997  else
3998  {
3999  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4000  }
4001  if((StationFlag) && (CumulativeLength == FrontElementLength))
4002  {
4003  ExitSpeedFull = 0;
4004  // force a stop for station (not for buffers or red signal)
4005  }
4006  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4007  }
4008  // new condition at v2.4.0
4009  else if(PowerAtRail <= 1)
4010  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4011  // avoid using AValue in denominator or have excessively long times
4012  {
4013  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4014  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4015  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4016 
4017  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4018  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4019  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4020  }
4021  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4022  // without the power need above condition or have hours of delay times, above added at v2.4.0
4023  {
4024  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4025  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4026  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4027  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4028  BrakeRate = 0;
4029  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4030  0.333334);
4031  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4032  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4034  // can accelerate continually over the half length
4035  {
4036  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4037  / 86400.0);
4039  // can accelerate continually over the full length
4040  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4041  {
4042  ExitTimeFull =
4043  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4044  }
4045  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4046  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4047  {
4048  // added at v0.6 as a safeguard
4049  if(MaxExitSpeed < EntrySpeed)
4050  {
4052  }
4053  // to prevent DeltaExitTimeToMaxInSecs being negative
4054  if(MaxExitSpeed < 1)
4055  {
4056  MaxExitSpeed = 1;
4057  }
4058  // to prevent divide by zero error
4059  // below as was
4061  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4062  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4063  (1.5 * AValue * AValue);
4064  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4065  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4066  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4067  }
4068  }
4069  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4070  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4071  // second halves of the element
4072  {
4073  // added at v0.6 as a safeguard
4074  if(MaxExitSpeed < EntrySpeed)
4075  {
4077  }
4078  // to prevent DeltaExitTimeToMaxInSecs being negative
4079  if(MaxExitSpeed < 1)
4080  {
4081  MaxExitSpeed = 1; // to prevent divide by zero error
4082  }
4083  // below as was
4085  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4086  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4087  (1.5 * AValue * AValue);
4088  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4089  // remaining distance to half length
4090  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4091  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4093  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4094  }
4095  }
4096  Utilities->CallLogPop(706);
4097  return;
4098  }
4099  else
4100  {
4101  if(!BuffersOrContinuationNowFlag)
4102  {
4103  if(NextSpeedLimit < LimitingSpeed)
4104  {
4105  LimitingSpeed = NextSpeedLimit;
4106  }
4107  }
4108  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4109  int TempMaxExitSpeed;
4110  // calc current value & if less than MaxExitSpeed set that to this
4111  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4112  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4113  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4114  {
4115  MaxExitSpeedAtHalfBraking = 0;
4116  }
4117  else
4118  {
4119  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4120  }
4121  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4122  {
4123  TempMaxExitSpeed = FrontElementMaxSpeed;
4124  }
4125  else
4126  {
4127  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4128  }
4129  if(TempMaxExitSpeed < MaxExitSpeed)
4130  {
4131  MaxExitSpeed = TempMaxExitSpeed;
4132  }
4133  // MaxExitSpeed is an external variable & this can reduce its value
4134  if(EntrySpeed > LimitingSpeed)
4135  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4136  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4137  {
4138  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4139  if(TempBrakeRate > MaxBrakeRate)
4140  {
4141  TempBrakeRate = MaxBrakeRate;
4142  }
4143  // shouldn't be for speedlimits since all known in advance, but include anyway
4144  if(TempBrakeRate > BrakeRate)
4145  {
4146  BrakeRate = TempBrakeRate;
4147  }
4148  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4149  }
4150  }
4151  if(!BuffersOrContinuationNowFlag)
4152  {
4153  CurrentTrackVectorPosition = NextTrackVectorPosition;
4154  EntryPos = NextEntryPos;
4155  CurrentElementHalfLength = NextElementHalfLength;
4156  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4157  {
4158  ContinuationNextFlag = true;
4159  }
4160  }
4161  }
4162  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4164  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4165  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4166  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4167  // stopping distance after it.
4168 
4169  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4170  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4171 
4172  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4173  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4174  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4175  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4176  // too late
4177 
4178  // set final braking or acc'n speed & time values
4179  if(BrakeRate > 0.01)
4180  {
4181  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4182  if(ExitSpeedHalfSquared < 10)
4183  {
4184  ExitSpeedHalf = 0;
4185  }
4186  else
4187  {
4188  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4189  }
4190  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4191  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4192  if(ExitSpeedFullSquared < 10)
4193  {
4194  ExitSpeedFull = 0;
4195  }
4196  else
4197  {
4198  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4199  }
4200  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4201  }
4202  else
4203  {
4204  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4205  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4206  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4207  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4208 
4209  BrakeRate = 0;
4210  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4211  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4212  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4214  {
4215  if(PowerAtRail > 1)
4216  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4217  {
4218  // [km/h/3.6 = m/s]
4219  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4220  / 86400.0);
4221  }
4222  else
4223  {
4224  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4225  }
4227  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4228  {
4229  if(PowerAtRail > 1)
4230  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4231  {
4232  // [km/h/3.6 = m/s]
4233  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4234  / 86400.0);
4235  }
4236  else
4237  {
4238  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4239  }
4240  }
4241  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4242  {
4243  // added at v0.6 as a safeguard
4244  if(MaxExitSpeed < EntrySpeed)
4245  {
4247  }
4248  // to prevent DeltaExitTimeToMaxInSecs being negative
4249  if(MaxExitSpeed < 1)
4250  {
4251  MaxExitSpeed = 1; // to prevent divide by zero error
4252  }
4253  // below as was
4255  double DeltaExitTimeToMaxInSecs;
4256  double DistanceToMax;
4257  if(PowerAtRail > 1) // added at v2.4.0
4258  {
4259  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4260  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4261  (1.5 * AValue * AValue);
4262  }
4263  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4264  {
4265  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4266  // these not really accurate but will be good enough
4267  DistanceToMax = EntryHalfLength;
4268  }
4269  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4270  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4271  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4272  }
4273  }
4274  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4275  {
4276  // added at v0.6 as a safeguard
4277  if(MaxExitSpeed < EntrySpeed)
4278  {
4280  }
4281  // to prevent DeltaExitTimeToMaxInSecs being negative
4282  if(MaxExitSpeed < 1)
4283  {
4284  MaxExitSpeed = 1; // to prevent divide by zero error
4285  }
4286  // below as was
4288  double DeltaExitTimeToMaxInSecs;
4289  double DistanceToMax;
4290  if(PowerAtRail > 1) // added at v2.4.0
4291  {
4292  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4293  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4294  (1.5 * AValue * AValue);
4295  }
4296  else
4297  {
4298  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4299  // these not really accurate but will be good enough
4300  DistanceToMax = EntryHalfLength / 2;
4301  }
4302  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4303  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4304  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4306  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4307  }
4308  }
4309  }
4310 
4311  else // SPADFlag set
4312  {
4314  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4315  if(ExitSpeedHalfSquared < 10)
4316  {
4317  ExitSpeedHalf = 0;
4318  }
4319  else
4320  {
4321  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4322  }
4323  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4324  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4325  if(ExitSpeedFullSquared < 10)
4326  {
4327  ExitSpeedFull = 0;
4328  }
4329  else
4330  {
4331  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4332  }
4333  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4334 
4335  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4336  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4337  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4338  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4339  // will be the stopping speed.
4340  if(ExitSpeedFull > 0)
4341  {
4342  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4343  {
4344  if((EntryPos == 0) || (EntryPos == 2))
4345  {
4346  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4347  {
4348  ExitPos = 1;
4349  }
4350  else
4351  {
4352  ExitPos = 3;
4353  }
4354  }
4355  else
4356  {
4357  ExitPos = 0;
4358  }
4359  }
4360  else
4361  {
4362  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4363  }
4364  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4365  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4366  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4367  {
4368  int NextElementLength;
4369  if(NextEntryPos > 1)
4370  {
4371  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4372  }
4373  else
4374  {
4375  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4376  }
4377  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4378  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4379  {
4380  ExitSpeedFull = 0;
4381  }
4382  }
4383  }
4384  }
4385  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4386  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4387  {
4388  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4389  if(LeadElement > -1)
4390  {
4392  // don't stop on a continuation either entering or leaving
4393  {
4396  MaxExitSpeed = LimitingSpeed;
4397  BrakeRate = 0;
4398  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4399  // assume length is 50m for ease of calc
4400  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4401  Utilities->CallLogPop(2126);
4402  return;
4403  }
4404  }
4405  else if(MidElement > -1)
4406  {
4408  {
4411  MaxExitSpeed = LimitingSpeed;
4412  BrakeRate = 0;
4413  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4414  // assume length is 50m for ease of calc
4415  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4416  Utilities->CallLogPop(2127);
4417  return;
4418  }
4419  }
4420  else if(LagElement > -1)
4421  {
4423  {
4426  MaxExitSpeed = LimitingSpeed;
4427  BrakeRate = 0;
4428  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4429  // assume length is 50m for ease of calc
4430  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4431  Utilities->CallLogPop(2128);
4432  return;
4433  }
4434  }
4435  if(EntrySpeed > 7.5) // keep going for at least another element
4436  {
4437  ExitSpeedHalf = EntrySpeed - 2.5;
4438  ExitSpeedFull = EntrySpeed - 5;
4439  MaxExitSpeed = LimitingSpeed;
4440  BrakeRate = 0;
4441  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4442  // assume length is 50m for ease of calc
4443  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4444  Utilities->CallLogPop(2129);
4445  return;
4446  }
4447  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4448  {
4449  // will appear to have slowed at steady rate
4450  ExitSpeedHalf = 0;
4451  ExitSpeedFull = 0;
4452  MaxExitSpeed = LimitingSpeed;
4453  BrakeRate = 0;
4454  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4455  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4456  StoppedWithoutPower = true;
4457  Utilities->CallLogPop(2130);
4458  return;
4459  }
4460  }
4461  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4462  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4463  Utilities->CallLogPop(707);
4464 }
4465 // ---------------------------------------------------------------------------
4466 /*
4467  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4468  {
4469  int NextExitPos;
4470  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4471  if(TimetableVector.empty()) return false;
4472  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4473  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4474  {
4475  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4476  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4477  {
4478  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4479  }
4480  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4481  {
4482  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4483  }
4484  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4485  NextElement = TempElement;
4486  }
4487  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4488  if(NextElement.TrackType == Buffers) return true;
4489  return false;//shouldn't reach here but include to prevent compiler return warning
4490  }
4491 */
4492 // ---------------------------------------------------------------------------
4493 
4494 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4495 /*
4496  returns the number by which the train ActionVectorEntryPtr needs
4497  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4498  actions when a stop or pass location has been reached before other timetabled actions have been carried out. If can't find it, or Name
4499  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4500  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4501  or pass (false) the location.
4502 */{
4503  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4504  Stop = false;
4505  if(TimetableFinished || (Name == ""))
4506  {
4507  Utilities->CallLogPop(957);
4508  return(-1);
4509  }
4510  // start looking from current pointer position
4511  for(TActionVectorEntry * Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4512  {
4513  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4514  {
4515  break;
4516  }
4517  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4518  {
4519  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4520  {
4521  Stop = true;
4522  Utilities->CallLogPop(960);
4523  return (Ptr - ActionVectorEntryPtr);
4524  }
4525  }
4526  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4527  {
4528  Utilities->CallLogPop(1517);
4529  return (Ptr - ActionVectorEntryPtr);
4530  }
4531  }
4532  Utilities->CallLogPop(959);
4533  return(-1); // not found a valid entry
4534 }
4535 
4536 // ---------------------------------------------------------------------------
4537 
4539 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4540  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4541  Ignores the call-on signal.
4542 */{
4543  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4544  int ReturnVal = 0;
4545 
4546  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4547  {
4548  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4549  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4550  }
4551  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4552  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4553 
4554  while(true)
4555  {
4556  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4557  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4558  {
4559  ReturnVal = 1;
4560  break;
4561  }
4562  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4563  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4564  {
4565  ReturnVal = 2;
4566  break;
4567  }
4568  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4569  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4570  {
4571  ReturnVal = 3;
4572  break;
4573  }
4574  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388,
4575  CurrentTrackVectorPosition).TrackType == Crossover))
4576  {
4577  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4578  // must be a loop - reached same point as examined earlier
4579  {
4580  ReturnVal = 4;
4581  break;
4582  }
4583  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4584  {
4585  ReturnVal = 4;
4586  break;
4587  }
4588  }
4589  else
4590  {
4591  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526,
4592  CurrentTrackVectorPosition).TempTrackMarker23))
4593  {
4594  ReturnVal = 4;
4595  break;
4596  }
4597  }
4598  if(EntryPos < 2)
4599  {
4600  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4601  }
4602  else
4603  {
4604  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4605  }
4606  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4607  {
4608  if((EntryPos == 0) || (EntryPos == 2))
4609  {
4610  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4611  {
4612  ExitPos = 1;
4613  }
4614  else
4615  {
4616  ExitPos = 3;
4617  }
4618  }
4619  else
4620  {
4621  ExitPos = 0;
4622  }
4623  }
4624  else
4625  {
4626  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4627  }
4628  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4629  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4630  CurrentTrackVectorPosition = NextTrackVectorPosition;
4631  EntryPos = NextEntryPos;
4632  }
4633  if(ReturnVal == 1)
4634  {
4635  Utilities->CallLogPop(708);
4636  return(false);
4637  }
4638  if(ReturnVal == 2)
4639  {
4640  Utilities->CallLogPop(709);
4641  return(true);
4642  }
4643  if(ReturnVal == 3)
4644  {
4645  Utilities->CallLogPop(946);
4646  return(true);
4647  }
4648  if(ReturnVal == 4)
4649  {
4650  Utilities->CallLogPop(947);
4651  return(true);
4652  }
4653  else
4654  {
4655  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4656  }
4657 }
4658 
4659 // ---------------------------------------------------------------------------
4660 
4662 /*
4663  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4664  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4665  change of direction (cdt), remaining here (Frh), or under signaller control);
4666  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4667  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4668  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4669  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] and l) no existing route conflicts with the route into the platform
4670  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4671  change points outside the route or have a route conflict if another route is set.
4672 */{
4673  if(Track->RouteFlashFlag)
4674  {
4675  return(false); // don't want to create a new route from the stop signal if one is already in construction as
4676  }
4677  // some of the callingon route elements may be involved
4678  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4679  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4680  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4681  int RouteStartPosition;
4682  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4683  int PlatformPosition;
4684  // the track vector position of the first stop platfrom
4685  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4686  // not used here
4687  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4688  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4689  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4690  // must be stopped at a signal but not at a location & still in timetable (a)
4692  // no need to check for SignallerStopped as this function only called in Timetable mode
4693  {
4694  Utilities->CallLogPop(711);
4695  return(false);
4696  }
4697  while(true)
4698  {
4699  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4700  // don't look further than 4km ahead (j)
4701  if(Distance > (4000 + LeadElementDistance))
4702  {
4703  Utilities->CallLogPop(967);
4704  return(false);
4705  }
4706  // if find another train on an element in front, before find a valid platform, return false (c)
4707  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4708  {
4709  Utilities->CallLogPop(713);
4710  return(false);
4711  }
4712  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4713  // be facing later on)
4714  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4715  {
4716  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4717  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4718  if(OtherTrain.LeadElement == -1)
4719  {
4720  Utilities->CallLogPop(714);
4721  return(false);
4722  }
4723  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4724  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4725  {
4726  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4727  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4728  (OtherTrain.TrainMode == Signaller))
4729  {
4730  break;
4731  }
4732  else
4733  {
4734  Utilities->CallLogPop(955);
4735  return(false);
4736  }
4737  }
4738  else // (h)
4739  {
4740  break;
4741  }
4742  }
4743  // if reach buffers or exit continuation return false (can set route)
4744  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4745  {
4746  Utilities->CallLogPop(716);
4747  return(false);
4748  }
4749  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4750  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4752  {
4753  Utilities->CallLogPop(717);
4754  return(false);
4755  }
4756  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4757 /*
4758  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4759  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4760  {
4761  Utilities->CallLogPop(718);
4762  return false;
4763  }
4764 */
4765  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4766  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4767  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4768  {
4769  if(StopRequired)
4770  {
4771  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4772  {
4773  if(!PlatformFoundFlag)
4774  {
4775  PlatformPosition = CurrentTrackVectorPosition;
4776  }
4777  // ensure this only set once at first valid platform position - the unrestricted route will end here
4778  PlatformFoundFlag = true;
4779  }
4780  }
4781  }
4782  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4783  // train has to be at station but that has to be before the next forward signal
4784 /*
4785  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4786  {
4787  Utilities->CallLogPop(719);
4788  return false;
4789  }
4790 */
4791  // make sure points are followed correctly (d) & set ExitPos
4792  if(CurrentTrackElement.TrackType == Points)
4793  {
4794  if((EntryPos == 0) || (EntryPos == 2))
4795  {
4796  if(CurrentTrackElement.Attribute == 0)
4797  {
4798  ExitPos = 1;
4799  }
4800  else
4801  {
4802  ExitPos = 3;
4803  }
4804  }
4805  if(EntryPos == 1)
4806  {
4807  if(CurrentTrackElement.Attribute == 0)
4808  {
4809  ExitPos = 0;
4810  }
4811  else
4812  {
4813  Utilities->CallLogPop(720);
4814  return(false);
4815  }
4816  }
4817  if(EntryPos == 3)
4818  {
4819  if(CurrentTrackElement.Attribute == 1)
4820  {
4821  ExitPos = 0;
4822  }
4823  else
4824  {
4825  Utilities->CallLogPop(721);
4826  return(false);
4827  }
4828  }
4829  }
4830  else
4831  {
4832  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4833  }
4834  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4835  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4836  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4837  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4838  if(ElementNumber < 2)
4839  {
4840  SkipRouteCheck = true;
4841  }
4842  else
4843  {
4844  SkipRouteCheck = false;
4845  }
4846  if(ElementNumber == 1) // the stop signal
4847  {
4848  RouteStartPosition = CurrentTrackVectorPosition;
4849  }
4850 /*
4851  if(ElementNumber == 2)
4852  {
4853  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4854  else AutoSigs = false;
4855  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4856  }
4857 */
4858  if(ElementNumber > 1)
4859  {
4860  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4861  {
4862  RouteOrPartRouteSet = true;
4863  }
4864  else
4865  {
4866  RouteOrPartRouteSet = false;
4867  }
4868  }
4869  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4870  {
4871  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4872  {
4873  Utilities->CallLogPop(1859);
4874  return(false);
4875  }
4876  int ExitLink = CurrentTrackElement.Link[ExitPos];
4877  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4878  {
4879  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4880  {
4881  Utilities->CallLogPop(1850);
4882  return(false);
4883  }
4884  }
4885  }
4886  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
4887  if(EntryPos < 2)
4888  {
4889  Distance += CurrentTrackElement.Length01;
4890  }
4891  else
4892  {
4893  Distance += CurrentTrackElement.Length23;
4894  }
4895  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
4896  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
4897  CurrentTrackVectorPosition = NextTrackVectorPosition;
4898  EntryPos = NextEntryPos;
4899  ElementNumber++;
4900  } // while(true)
4901 
4902  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
4903  // from the stop signal (note that it may be last in an autosigs route)
4904  // a single element route at the stop signal should have been removed prior to this function being called (that called before
4905  // this in ClockTimer2)
4906 
4907  // now add elements to the CallonVector
4908  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
4909 
4910  AllRoutes->CallonVector.push_back(CallonEntry);
4911  Utilities->CallLogPop(1860);
4912  return(true); // return false if fail to set route for any reason
4913 }
4914 
4915 // ---------------------------------------------------------------------------
4916 /*
4917  bool TTrain::TimetableFinished()
4918  {
4919  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
4920  {
4921  return true;
4922  }
4923  return false;
4924  }
4925 */
4926 // ---------------------------------------------------------------------------
4927 
4928 AnsiString TTrain::GetTrainHeadCode(int Caller)
4929 
4930 {
4931  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
4932  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
4933 
4934  Utilities->CallLogPop(1452);
4935  return(RepeatHeadCode);
4936 }
4937 
4938 // ---------------------------------------------------------------------------
4939 
4940 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
4941 {
4942  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
4943  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
4944 
4945  Utilities->CallLogPop(1453);
4946  return(RepeatTime);
4947 }
4948 
4949 // ---------------------------------------------------------------------------
4950 
4951 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
4952 {
4953  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
4954  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
4955  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
4956  // first check that train is fully on the railway
4957  bool FrontValid = false, RearValid = false;
4958  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
4959 
4960  if((LeadElement == -1) || (MidElement == -1))
4961  {
4962  TrainToBeJoinedBy = NULL;
4963  Utilities->CallLogPop(2131);
4964  return(false);
4965  }
4967  {
4968  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
4969  FrontValid = true;
4970  }
4972  {
4973  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
4974  RearValid = true;
4975  }
4976  int TrainToBeJoinedByID = -1;
4977 
4978  // first check if on a 2-track element & select correct ID number if so
4979  if(FrontValid)
4980  {
4981  if(FrontAdjacentTrackElement.TrackType == Bridge)
4982  {
4984  {
4985  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
4986  }
4987  else
4988  {
4989  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
4990  }
4991  }
4992  else
4993  {
4994  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
4995  }
4996  }
4997  if((TrainToBeJoinedByID < 0) && RearValid)
4998  {
4999  // first check if on a 2-track element & select correct ID number if so
5000  if(RearAdjacentTrackElement.TrackType == Bridge)
5001  {
5003  {
5004  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5005  }
5006  else
5007  {
5008  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5009  }
5010  }
5011  else
5012  {
5013  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5014  }
5015  }
5016  if(TrainToBeJoinedByID < 0) // no adjacent train
5017  {
5018  TrainToBeJoinedBy = NULL;
5019  Utilities->CallLogPop(2132);
5020  return(false);
5021  }
5022  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5023  if(!TrainToBeJoinedBy->Stopped())
5024  {
5025  TrainToBeJoinedBy = NULL;
5026  Utilities->CallLogPop(2133);
5027  return(false);
5028  }
5029  Utilities->CallLogPop(2134);
5030  return(true);
5031 }
5032 
5033 // ---------------------------------------------------------------------------
5034 
5035 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5036  TDateTime TimetableNonRepeatTime, bool Warning)
5037 /*
5038  Time = timetable time, the time adjustments for repeat trains is carried out internally
5039  Not all messages need this, if not needed a dummy value is required but not used
5040 
5041  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5042  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5043  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5044  //NB for Frh just give terminated message but without event time - don't use this function
5045  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5046  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5047  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5048  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5049  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5050  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5051  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5052  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5053  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5054  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5055  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5056  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5057  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5058  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5059  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5060  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5061  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
5062  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5063  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5064  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5065  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5066  SignallerStop 06:05:40: 2F46 stopped on signaller command
5067  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5068  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5069 */{
5070  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5071  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5072  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5073  int IntMinsLate = 0;
5074 
5075  // need to set it in case MinsLate == 0, since it isn't tested for that
5076  if(ActionType == Arrive)
5077  {
5078  ActionLog = " arrived at ";
5079  }
5080  if(ActionType == Terminate)
5081  {
5082  if(TerminatedMessageSent) // to avoid it being sent twice
5083  {
5084  Utilities->CallLogPop(1104);
5085  return;
5086  }
5087  ActionLog = " terminated at ";
5088  TerminatedMessageSent = true;
5089  }
5090  if(ActionType == Depart)
5091  {
5092  ActionLog = " departed from ";
5093  }
5094  if(ActionType == Pass)
5095  {
5096  ActionLog = " passed ";
5097  }
5098  if(ActionType == Create)
5099  {
5100  ActionLog = " created at ";
5101  }
5102  if(ActionType == Enter)
5103  {
5104  ActionLog = " entered railway at ";
5105  }
5106  if(ActionType == Leave)
5107  {
5108  ActionLog = " left railway at ";
5109  }
5110  if(ActionType == FrontSplit)
5111  {
5112  ActionLog = " split from front to ";
5113  }
5114  if(ActionType == RearSplit)
5115  {
5116  ActionLog = " split from rear to ";
5117  }
5118  if(ActionType == JoinedByOther)
5119  {
5120  ActionLog = " joined by ";
5121  }
5122  if(ActionType == ChangeDirection)
5123  {
5124  ActionLog = " changed direction at ";
5125  }
5126  if(ActionType == NewService)
5127  {
5128  ActionLog = " became new service ";
5129  }
5130  if(ActionType == TakeSignallerControl)
5131  {
5132  ActionLog = " taken under signaller control at ";
5133  }
5134  if(ActionType == RestoreTimetableControl)
5135  {
5136  ActionLog = " restored to timetable control at ";
5137  }
5138  if(ActionType == RemoveTrain)
5139  {
5140  if(Crashed)
5141  {
5142  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5143  }
5144  else if(Derailed)
5145  {
5146  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5147  }
5148  else
5149  {
5150  ActionLog = " REMOVED FROM RAILWAY at ";
5151  }
5152  }
5153  if(ActionType == SignallerMoveForwards)
5154  {
5155  ActionLog = " received signaller authority to proceed";
5156  }
5157  if(ActionType == SignallerStepForward)
5158  {
5159  ActionLog = " received signaller authority to step forward";
5160  }
5161  if(ActionType == SignallerChangeDirection)
5162  {
5163  ActionLog = " changed direction under signaller control at ";
5164  }
5165  if(ActionType == SignallerPassRedSignal)
5166  {
5167  ActionLog = " received signaller authority to pass red signal";
5168  }
5169  if(ActionType == SignallerControlStop)
5170  {
5171  ActionLog = " received signaller instruction to stop";
5172  }
5173  if(ActionType == SignallerStop)
5174  {
5175  ActionLog = " stopped on signaller instruction ";
5176  }
5177  if(ActionType == SignallerJoin)
5178  {
5179  ActionLog = " joined under signaller control by ";
5180  }
5181  if(ActionType == TrainFailure)
5182  {
5183  ActionLog = " suffered an onboard power failure at ";
5184  }
5185  if(ActionType == RepairFailedTrain)
5186  {
5187  ActionLog = " failure repaired at ";
5188  }
5189  if(ActionType == SignallerLeave)
5190  {
5191  ActionLog = " left railway under signaller control at ";
5192  }
5193  if(OtherHeadCode != "")
5194  {
5195  OtherHeadCode += " at ";
5196  }
5197  TDateTime ActualTime = TrainController->TTClockTime;
5198 
5199  if(Warning)
5200  {
5201  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5202  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
5203  }
5204  else
5205  {
5206  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5207  }
5208  bool TimePerformance = true;
5209 
5210  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5211  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5212  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5213  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5214  // SignallerJoin & RepairFailedTrain new at v2.4.0
5215  {
5216  TimePerformance = false;
5217  }
5218  if(TimePerformance)
5219  {
5220  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5221  MinsDelayed = float(MinsLate);
5222  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5223  {
5224  MinsDelayed = 0;
5225  }
5226  // new v2.2.0 for OpActionPanel, can be positive or negative
5227  if(ActionType == Arrive)
5228  {
5230  }
5231  // since train has just arrived this value is the
5232  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5233  // subtracted from later stop recoverable times.
5234  if(MinsLate < 0)
5235  {
5236  IntMinsLate = int(ceil(MinsLate));
5237  }
5238  if(MinsLate > 0)
5239  {
5240  IntMinsLate = int(floor(MinsLate));
5241  }
5242  if(IntMinsLate == 0)
5243  {
5244  PerfLog = " on time";
5245  }
5246  else if(IntMinsLate == 1)
5247  {
5248  PerfLog = " 1 minute late";
5249  }
5250  else if(IntMinsLate == -1)
5251  {
5252  PerfLog = " 1 minute early";
5253  }
5254  else if(IntMinsLate > 1)
5255  {
5256  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5257  }
5258  else if(IntMinsLate < -1)
5259  {
5260  int PosIntMinsLate = -IntMinsLate;
5261  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5262  }
5263  if(LocationName.Pos('-') > 0)
5264  {
5265  PerfLog = "," + PerfLog;
5266  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5267  }
5268  Display->PerformanceLog(0, BaseLog + PerfLog);
5269  }
5270  else
5271  {
5272  Display->PerformanceLog(1, BaseLog);
5273  }
5274  if(Warning)
5275  {
5276  Display->WarningLog(0, WarningBaseLog);
5277  }
5278  // update statistics
5279  if((ActionType == Arrive) && (IntMinsLate == 0))
5280  {
5282  }
5283  else if((ActionType == Arrive) && (IntMinsLate > 0))
5284  {
5286  TrainController->TotLateArrMins += IntMinsLate;
5287  }
5288  else if((ActionType == Arrive) && (IntMinsLate < 0))
5289  {
5291  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5292  }
5293 
5294  else if((ActionType == Pass) && (IntMinsLate == 0))
5295  {
5297  }
5298  else if((ActionType == Pass) && (IntMinsLate > 0))
5299  {
5301  TrainController->TotLatePassMins += IntMinsLate;
5302  }
5303  else if((ActionType == Pass) && (IntMinsLate < 0))
5304  {
5306  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5307  }
5308 
5309  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5310  {
5312  }
5313  else if((ActionType == Leave) && (IntMinsLate > 0))
5314  {
5316  TrainController->TotLateExitMins += IntMinsLate;
5317  }
5318  else if((ActionType == Leave) && (IntMinsLate < 0))
5319  {
5321  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5322  }
5323 
5324  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5325  {
5327  }
5328  else if((ActionType == Depart) && (IntMinsLate > 0))
5329  {
5331  TrainController->TotLateDepMins += IntMinsLate;
5332  }
5333  Utilities->CallLogPop(968);
5334 }
5335 
5336 // ---------------------------------------------------------------------------
5337 
5338 void TTrain::TrainHasFailed(int Caller)
5339 {
5340  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5341  if(Crashed || Derailed || DerailPending)
5342  {
5343  TrainFailurePending = false;
5344  Utilities->CallLogPop(2135);
5345  return;
5346  }
5347  AnsiString LocName = "";
5348 
5349  if(LeadElement > -1)
5350  {
5352  }
5353  if((LocName == "") && (MidElement > -1))
5354  {
5356  }
5357  if((LocName == "") && LeadElement > -1)
5358  {
5359  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5360  }
5361  if((LocName == "") && (MidElement > -1))
5362  {
5363  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5364  }
5365  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5366  TrainFailed = true;
5367  TrainFailurePending = false;
5369  PowerAtRail = 0.08;
5370  AValue = sqrt(2 * PowerAtRail / Mass);
5372  // TrainFailed only called when PlotElements properly set to lead, Mid & Lag elements
5373  if(Stopped())
5374  {
5375  EntrySpeed = 0;
5376  ExitSpeedHalf = 0;
5377  ExitSpeedFull = 0;
5378  MaxExitSpeed = 0;
5379  BrakeRate = 0;
5380  StoppedWithoutPower = true;
5381  }
5383  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5384  // true for warning, TDateTime not used
5385  Utilities->CallLogPop(2136);
5386 }
5387 
5388 // ---------------------------------------------------------------------------
5389 
5390 void TTrain::FrontTrainSplit(int Caller)
5391 {
5392 /*
5393  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5394  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5395  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5396  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5397 */
5398  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5399  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5400  if(PowerAtRail < 1)
5401  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5402  {
5404  {
5405  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5406  }
5408  Utilities->CallLogPop(2137);
5409  return;
5410  }
5411  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5412 
5413  if(LocationName == "")
5414  {
5415  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5416  }
5417  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5418  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5419  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5421 
5422  // determine all positions & exits
5423  if(LocationName != "")
5424  {
5425  // if message given only call at ~5 sec intervals
5427  {
5428  FirstNamedElementPos = LeadElement;
5429  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5430  // check if possible with LeadElement as First
5431  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5432  {
5433  FirstNamedElementPos = MidElement;
5434  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5435  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5436  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5437  {
5439  {
5440  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5442  }
5443  Utilities->CallLogPop(1009);
5444  return;
5445  }
5446  }
5447  else
5448  {
5449  // if first is possible then check if all 4 positions at location, and if not try the second
5450  int LeadPosA = FirstNamedElementPos;
5451  int LeadPosB = FirstNamedLinkedElementPos;
5452  int LeadPosC = SecondNamedElementPos;
5453  int LeadPosD = SecondNamedLinkedElementPos;
5454  // count number of positions that are at the location
5455  int LeadNumAtLoc = 0;
5456  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5457  {
5458  LeadNumAtLoc++;
5459  }
5460  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5461  {
5462  LeadNumAtLoc++;
5463  }
5464  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5465  {
5466  LeadNumAtLoc++;
5467  }
5468  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5469  {
5470  LeadNumAtLoc++;
5471  }
5472  if(LeadNumAtLoc < 4)
5473  {
5474  FirstNamedElementPos = MidElement;
5475  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5476  SecondNamedLinkedElementPos)) // restore originals
5477  {
5478  FirstNamedElementPos = LeadPosA;
5479  FirstNamedLinkedElementPos = LeadPosB;
5480  SecondNamedElementPos = LeadPosC;
5481  SecondNamedLinkedElementPos = LeadPosD;
5482  }
5483  else // count number at location
5484  {
5485  int MidNumAtLoc = 0;
5486  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5487  {
5488  MidNumAtLoc++;
5489  }
5490  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5491  {
5492  MidNumAtLoc++;
5493  }
5494  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5495  {
5496  MidNumAtLoc++;
5497  }
5498  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5499  {
5500  MidNumAtLoc++;
5501  }
5502  if(LeadNumAtLoc > MidNumAtLoc)
5503  // change back, else keep new values
5504  {
5505  FirstNamedElementPos = LeadPosA;
5506  FirstNamedLinkedElementPos = LeadPosB;
5507  SecondNamedElementPos = LeadPosC;
5508  SecondNamedLinkedElementPos = LeadPosD;
5509  }
5510  }
5511  }
5512  }
5513  }
5514  else
5515  {
5516  Utilities->CallLogPop(1791);
5517  return;
5518  }
5519  }
5520  else
5521  {
5522  throw Exception("Error - LocationName not set in FrontTrainSplit");
5523  }
5524  // set front & rear train parameters
5525  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5526  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5527  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5528  if(LeadElement == FirstNamedElementPos)
5529  {
5530  if(MidElement == SecondNamedElementPos)
5531  {
5532  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5533  FrontTrainRearPosition = FirstNamedElementPos;
5534  RearTrainFrontPosition = SecondNamedElementPos;
5535  RearTrainRearPosition = SecondNamedLinkedElementPos;
5536  }
5537  else // MidElement must == FirstNamedLinkedElementPos
5538  {
5539  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5540  FrontTrainRearPosition = SecondNamedElementPos;
5541  RearTrainFrontPosition = FirstNamedElementPos;
5542  RearTrainRearPosition = FirstNamedLinkedElementPos;
5543  }
5544  }
5545  else // MidElement == FirstNamedElementPos
5546  {
5547  if(LeadElement == SecondNamedElementPos)
5548  {
5549  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5550  FrontTrainRearPosition = SecondNamedElementPos;
5551  RearTrainFrontPosition = FirstNamedElementPos;
5552  RearTrainRearPosition = FirstNamedLinkedElementPos;
5553  }
5554  else // LeadElement must == FirstNamedLinkedElementPos
5555  {
5556  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5557  FrontTrainRearPosition = FirstNamedElementPos;
5558  RearTrainFrontPosition = SecondNamedElementPos;
5559  RearTrainRearPosition = SecondNamedLinkedElementPos;
5560  }
5561  }
5562  RearTrainExitPos = -1;
5563  for(int x = 0; x < 4; x++)
5564  {
5565  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5566  {
5567  RearTrainExitPos = x;
5568  break;
5569  }
5570  }
5571  if(RearTrainExitPos == -1)
5572  {
5573  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5574  }
5575  FrontTrainExitPos = -1;
5576  for(int x = 0; x < 4; x++)
5577  {
5578  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5579  {
5580  FrontTrainExitPos = x;
5581  break;
5582  }
5583  }
5584  if(FrontTrainExitPos == -1)
5585  {
5586  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5587  }
5588  // check no train (apart from self) on any of the 4 elements & fail if so
5589  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5590  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5591 
5592  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5593  {
5594  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5595  }
5596  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5597  {
5598  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5599  }
5600  else
5601  {
5602  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5603  }
5604  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5605  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5606  // can't be a bridge
5607  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5608  // can't be a bridge
5609  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5610  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5611 
5612  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5613  {
5614  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5615  }
5616  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5617  {
5618  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5619  }
5620  else
5621  {
5622  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5623  }
5624  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5625  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5626  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5627  {
5629  {
5632  }
5633  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5634  Utilities->CallLogPop(1010);
5635  return;
5636  }
5638  {
5640  }
5641  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5642  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5643  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5644  // variable as it is needed for setting up the new train
5645  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5646 
5647  UnplotTrain(0);
5648  StartSpeed = 0;
5649  RearStartElement = RearTrainRearPosition;
5650  RearStartExitPos = RearTrainExitPos;
5651  StoppedAtLocation = true;
5652  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5653  {
5654  StoppedWithoutPower = true;
5655  }
5656  PlotStartPosition(3);
5661 
5662  Mass = Mass / 2;
5663  MaxBrakeRate = MaxBrakeRate / 2;
5664  PowerAtRail = PowerAtRail / 2;
5665  AValue = sqrt(2 * PowerAtRail / Mass);
5666  // shouldn't change but include in case not set earlier
5667 
5668  // create new front train
5669 /*
5670  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5671  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5672  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5673 */
5674  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5675  TActionEventType EventType = NoEvent;
5676 
5677  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5678  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5679  // false for SignallerControl
5680  {
5681  Utilities->CallLogPop(1721); // EventType not used here
5682  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5683  // another train, in which case a message will have been sent to the perf log, also might well clear later
5684  // when other train moves away
5685  return;
5686  }
5687  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5688  // see mods in UpdateTrain for v1.3.2
5689  TrainController->TrainAdded = true;
5690 
5691  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5692 
5693  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5694  TTOD.RunningEntry = Running;
5695  Utilities->CallLogPop(998);
5696 }
5697 
5698 // ---------------------------------------------------------------------------
5699 
5700 void TTrain::RearTrainSplit(int Caller)
5701 {
5702 /*
5703  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5704  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5705  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5706  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5707 */
5708  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5709  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5710  if(PowerAtRail < 1)
5711  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5712  {
5714  {
5715  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5716  }
5718  Utilities->CallLogPop(2138);
5719  return;
5720  }
5721  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5722 
5723  if(LocationName == "")
5724  {
5725  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5726  }
5727  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5728  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5729  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5731 
5732  // determine all positions & exits
5733  if(LocationName != "")
5734  {
5735  // if message given only call at ~5 sec intervals
5737  {
5738  FirstNamedElementPos = LeadElement;
5739  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5740  SecondNamedLinkedElementPos))
5741  {
5742  FirstNamedElementPos = MidElement;
5743  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5744  SecondNamedLinkedElementPos))
5745  {
5747  {
5748  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5750  }
5751  Utilities->CallLogPop(1013);
5752  return;
5753  }
5754  }
5755  else
5756  {
5757  // if first is possible then check if all 4 positions at location, and if not try the second
5758  int LeadPosA = FirstNamedElementPos;
5759  int LeadPosB = FirstNamedLinkedElementPos;
5760  int LeadPosC = SecondNamedElementPos;
5761  int LeadPosD = SecondNamedLinkedElementPos;
5762  // count number of positions that are at the location
5763  int LeadNumAtLoc = 0;
5764  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5765  {
5766  LeadNumAtLoc++;
5767  }
5768  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5769  {
5770  LeadNumAtLoc++;
5771  }
5772  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5773  {
5774  LeadNumAtLoc++;
5775  }
5776  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5777  {
5778  LeadNumAtLoc++;
5779  }
5780  if(LeadNumAtLoc < 4)
5781  {
5782  FirstNamedElementPos = MidElement;
5783  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5784  SecondNamedLinkedElementPos)) // restore originals
5785  {
5786  FirstNamedElementPos = LeadPosA;
5787  FirstNamedLinkedElementPos = LeadPosB;
5788  SecondNamedElementPos = LeadPosC;
5789  SecondNamedLinkedElementPos = LeadPosD;
5790  }
5791  else // count number at location
5792  {
5793  int MidNumAtLoc = 0;
5794  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5795  {
5796  MidNumAtLoc++;
5797  }
5798  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5799  {
5800  MidNumAtLoc++;
5801  }
5802  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5803  {
5804  MidNumAtLoc++;
5805  }
5806  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5807  {
5808  MidNumAtLoc++;
5809  }
5810  if(LeadNumAtLoc > MidNumAtLoc)
5811  // change back, else keep new values
5812  {
5813  FirstNamedElementPos = LeadPosA;
5814  FirstNamedLinkedElementPos = LeadPosB;
5815  SecondNamedElementPos = LeadPosC;
5816  SecondNamedLinkedElementPos = LeadPosD;
5817  }
5818  }
5819  }
5820  }
5821  }
5822  else
5823  {
5824  Utilities->CallLogPop(1792);
5825  return;
5826  }
5827  }
5828  else
5829  {
5830  throw Exception("Error - LocationName not set in RearTrainSplit");
5831  }
5832  // set front & rear train parameters
5833  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5834  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5835  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5836  if(LeadElement == FirstNamedElementPos)
5837  {
5838  if(MidElement == SecondNamedElementPos)
5839  {
5840  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5841  FrontTrainRearPosition = FirstNamedElementPos;
5842  RearTrainFrontPosition = SecondNamedElementPos;
5843  RearTrainRearPosition = SecondNamedLinkedElementPos;
5844  }
5845  else // MidElement must == FirstNamedLinkedElementPos
5846  {
5847  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5848  FrontTrainRearPosition = SecondNamedElementPos;
5849  RearTrainFrontPosition = FirstNamedElementPos;
5850  RearTrainRearPosition = FirstNamedLinkedElementPos;
5851  }
5852  }
5853  else // MidElement == FirstNamedElementPos
5854  {
5855  if(LeadElement == SecondNamedElementPos)
5856  {
5857  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5858  FrontTrainRearPosition = SecondNamedElementPos;
5859  RearTrainFrontPosition = FirstNamedElementPos;
5860  RearTrainRearPosition = FirstNamedLinkedElementPos;
5861  }
5862  else // LeadElement must == FirstNamedLinkedElementPos
5863  {
5864  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5865  FrontTrainRearPosition = FirstNamedElementPos;
5866  RearTrainFrontPosition = SecondNamedElementPos;
5867  RearTrainRearPosition = SecondNamedLinkedElementPos;
5868  }
5869  }
5870  RearTrainExitPos = -1;
5871  for(int x = 0; x < 4; x++)
5872  {
5873  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5874  {
5875  RearTrainExitPos = x;
5876  break;
5877  }
5878  }
5879  if(RearTrainExitPos == -1)
5880  {
5881  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5882  }
5883  FrontTrainExitPos = -1;
5884  for(int x = 0; x < 4; x++)
5885  {
5886  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5887  {
5888  FrontTrainExitPos = x;
5889  break;
5890  }
5891  }
5892  if(FrontTrainExitPos == -1)
5893  {
5894  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
5895  }
5896  // check no train (apart from self) on any of the 4 elements & fail if so
5897  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5898  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
5899 
5900  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5901  {
5902  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5903  }
5904  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5905  {
5906  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5907  }
5908  else
5909  {
5910  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5911  }
5912  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5913  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
5914  // can't be a bridge
5915  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
5916  // can't be a bridge
5917  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5918  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
5919 
5920  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5921  {
5922  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5923  }
5924  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5925  {
5926  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5927  }
5928  else
5929  {
5930  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5931  }
5932  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5933  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5934  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5935  {
5937  {
5940  }
5941  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5942  Utilities->CallLogPop(1014);
5943  return;
5944  }
5946  {
5948  }
5949  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
5950  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5951  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5952  // variable as it is needed for setting up the new train
5953  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5954 
5955  UnplotTrain(1);
5956  StartSpeed = 0;
5957  RearStartElement = FrontTrainRearPosition;
5958  RearStartExitPos = FrontTrainExitPos;
5959  StoppedAtLocation = true;
5960  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5961  {
5962  StoppedWithoutPower = true;
5963  }
5964  PlotStartPosition(4);
5969  Mass = Mass / 2;
5970  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
5971  MaxBrakeRate = MaxBrakeRate / 2;
5972  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
5973  PowerAtRail = PowerAtRail / 2;
5974  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
5975  AValue = sqrt(2 * PowerAtRail / Mass);
5976  // shouldn't change but include in case not set earlier
5977 
5978  // create new rear train
5979 /*
5980  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5981  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5982  int RepeatNumber, int IncrementalMinutes)
5983 */
5984  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5985  TActionEventType EventType = NoEvent;
5986 
5987  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5988  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5989  // false for SignallerControl
5990  {
5991  Utilities->CallLogPop(1722); // EventType not used here
5992  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5993  // another train, in which case a message will have been sent to the perf log, also might well clear later
5994  // when other train moves away
5995  return;
5996  }
5997  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5998  // see mods in UpdateTrain for v1.3.2
5999  TrainController->TrainAdded = true;
6000  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6001 
6002  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6003  TTOD.RunningEntry = Running;
6004  Utilities->CallLogPop(1015);
6005 }
6006 
6007 // ---------------------------------------------------------------------------
6008 
6009 void TTrain::FinishJoin(int Caller)
6010 {
6011  if(FinishJoinLogSent == false)
6012  {
6013  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6014  FinishJoinLogSent = true; // so don't keep logging this event
6015  // don't need to reset it to false after the event as the train is deleted
6016  }
6017  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6018  if(TrainFailed)
6019  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6020  {
6022  {
6023  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6024  }
6026  Utilities->CallLogPop(2139);
6027  return;
6028  }
6029  if(TrainGone)
6030  // this means that the train has already joined the other & is awaiting deletion by TrainController
6031  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6032  // on from jbo & TrainToJoinIsAdjacent returns false
6033  {
6034  Utilities->CallLogPop(1035);
6035  return;
6036  }
6037  TTrain *TrainToJoin;
6039 
6040  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6041  {
6043  {
6044  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6047  }
6048  Utilities->CallLogPop(1030);
6049  return; // keep this here in case need to add code before final return
6050  }
6051  // no need to clear error report flag here, cleared in jbo function
6052  // No need to set TimetableFinished, done in jbo function
6053  Utilities->CallLogPop(1031);
6054 }
6055 
6056 // ---------------------------------------------------------------------------
6057 
6058 void TTrain::JoinedBy(int Caller)
6059 {
6060  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6061  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6062  if(PowerAtRail < 1)
6063  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6064  {
6066  {
6067  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6068  }
6070  Utilities->CallLogPop(2140);
6071  return;
6072  }
6073  TTrain *TrainToBeJoinedBy;
6075 
6076  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6077  {
6079  {
6080  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6083  }
6084  LastActionDelayFlag = true;
6085  // need to update LastActionTime if this train first to arrive as need 30s after
6086  // both adjacent before the join
6087  Utilities->CallLogPop(1032);
6088  return;
6089  }
6090  // here when other train is adjacent
6092  {
6094  // need to update this as need 30s after both adjacent before the join
6095  LastActionDelayFlag = false;
6096  Utilities->CallLogPop(1033);
6097  return;
6098  }
6099  // here when other train is adjacent & 30 secs elapsed since both adjacent
6100 
6101  // set new values for mass etc
6102  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6103  {
6104  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6105  }
6106  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6107  double OwnBrakeForce = MaxBrakeRate * Mass;
6108  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6109 
6110  Mass += TrainToBeJoinedBy->Mass;
6111  MaxBrakeRate = CombinedBrakeRate;
6112  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6113  AValue = sqrt(2 * PowerAtRail / Mass);
6114 
6116  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6117  TrainToBeJoinedBy->TimetableFinished = true;
6118  TrainToBeJoinedBy->TrainGone = true;
6119  // this will cause other train to be deleted
6120  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6124  Utilities->CallLogPop(1034);
6125 }
6126 
6127 // ---------------------------------------------------------------------------
6128 
6130 {
6131  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6132  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6133  if(PowerAtRail < 1)
6134  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6135  {
6137  {
6138  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6139  }
6140  ZeroPowerNoCDTMessage = true;
6141  Utilities->CallLogPop(2141);
6142  return;
6143  }
6144  TColor TempColour = BackgroundColour;
6145 
6146  UnplotTrain(2);
6149  StartSpeed = 0;
6150  StoppedAtLocation = true;
6151  PlotStartPosition(1);
6152  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6153  // plot same as was - should always be pale green
6157 
6158  //now erase a stub route if there is one, added at v2.5.1
6159  //first element of route is now immediately behind the train (i.e. next to MidElement)
6160  if(MidEntryPos >= 0)
6161  {
6162  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6163  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6164  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6165  int RouteNumber = -1;
6166  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6167  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6168  {
6169  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6170  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6171  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6172  {
6173  bool FirstPass = true; //added at v2.8.0
6174  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6175  {
6176  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6177  int TVPos2 = PDE.GetTrackVectorPosition();
6178  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6179  {
6180  break;
6181  }
6182  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6184  {
6185  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6186  }
6187  else
6188  {
6189  break;
6190  }
6191  FirstPass = false;
6192  }
6193  AllRoutes->RebuildRailwayFlag = true;
6194  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6195  }
6196  }
6197  }
6198  Utilities->CallLogPop(1012);
6199 }
6200 
6201 // ---------------------------------------------------------------------------
6202 
6203 void TTrain::NewTrainService(int Caller)
6204 // change to new train, give new service message
6205 {
6206  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6207  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6208  if(PowerAtRail < 1)
6209  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6210  {
6212  {
6213  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6214  }
6216  Utilities->CallLogPop(2142);
6217  return;
6218  }
6220 
6222  UnplotTrain(3);
6225  StartSpeed = 0;
6230  HeadCode = NewHeadCode;
6231  StoppedAtLocation = true;
6232  PlotStartPosition(5);
6234  // pale green
6237  TerminatedMessageSent = false;
6238  Utilities->CallLogPop(1022);
6239 }
6240 
6241 // ---------------------------------------------------------------------------
6242 
6243 void TTrain::RemainHere(int Caller)
6244 {
6245  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6246  if(RemainHereLogNotSent) // to prevent repeated logs
6247  {
6248  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6249  RemainHereLogNotSent = false;
6250  }
6252  {
6253  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
6256  TerminatedMessageSent = true;
6257  }
6258  TimetableFinished = true;
6259  Utilities->CallLogPop(1023);
6260 }
6261 
6262 // ---------------------------------------------------------------------------
6263 
6264 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6265 /*
6266  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6267  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6268  except where an action is a departure, starting at the current value for the pointer
6269  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6270  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6271 */{
6272  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6273  {
6274  return; // if remove train that starts under signaller control no messages needed
6275 
6276  }
6277  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6278  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6279  if(IncNum > 0)
6280  {
6281  for(int x = 0; x < IncNum; x++)
6282  {
6283  if(x > 0)
6284  {
6285  Ptr++;
6286  }
6287  // arrival - no need to test for termination as this section only covers missed actions up to the
6288  // arrival point - may terminate later but that not missed
6289  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6290  {
6292  }
6293  // arrival & departure
6294  if(Ptr->FormatType == TimeTimeLoc)
6295  {
6297  }
6298  // departure
6299  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6300  {
6301  continue; // skip TimeLoc departures, message given for arrivals
6302  }
6303  // pass
6304  else if(Ptr->FormatType == PassTime)
6305  {
6307  }
6308  // split
6309  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6310  {
6312  }
6313  // jbo
6314  else if(Ptr->Command == "jbo")
6315  {
6317  }
6318  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6319  // be starts, finishes or cdt
6320  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6321  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6322  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6323  (Ptr->FormatType == Repeat))
6324  {
6325  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6326  }
6327  }
6328  }
6329  else
6330  {
6331  bool IncludeFER = false;
6332  if(IncNum == -1)
6333  {
6334  IncludeFER = true;
6335  }
6336  while(true) // finish commands & repeats break out of loop
6337  {
6338  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6339  if(!IncludeFER && (Ptr->Command == "Fer"))
6340  {
6341  break;
6342  }
6343  // Fer & included
6344  else if(IncludeFER && (Ptr->Command == "Fer"))
6345  {
6347  break;
6348  }
6349  // Repeat
6350  else if(Ptr->FormatType == Repeat)
6351  {
6352  break;
6353  }
6354  // Fjo
6355  else if(Ptr->Command == "Fjo")
6356  {
6358  break;
6359  }
6360  // Frh
6361  else if(Ptr->Command == "Frh")
6362  {
6364  {
6366  TerminatedMessageSent = true;
6367  }
6368  break;
6369  }
6370  // Frh-sh
6371  else if(Ptr->Command == "Frh-sh")
6372  {
6374  {
6376  TerminatedMessageSent = true;
6377  }
6378  break;
6379  }
6380  // Fns, F-nshs, Fns-sh
6381  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6382  {
6384  break;
6385  }
6386  // end of breakout actions
6387  // arrival
6388  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6389  {
6390  if(IsTrainTerminating(1))
6391  {
6393  TerminatedMessageSent = true;
6394  }
6395  else
6396  {
6398  }
6399  }
6400  // arrival & departure
6401  else if(Ptr->FormatType == TimeTimeLoc)
6402  {
6404  }
6405  // departure
6406  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6407  {
6408  Ptr++;
6409  continue; // skip TimeLoc departures, message given for arrivals
6410  }
6411  // pass
6412  else if(Ptr->FormatType == PassTime)
6413  {
6415  }
6416  // split
6417  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6418  {
6420  }
6421  // jbo
6422  else if(Ptr->Command == "jbo")
6423  {
6425  }
6426  // cdt
6427  else if(Ptr->Command == "cdt")
6428  {
6430  }
6431  // Errors
6432  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6433  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6434  {
6435  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6436  }
6437  Ptr++;
6438  }
6439  TimetableFinished = true;
6440  }
6441  Utilities->CallLogPop(1021);
6442 }
6443 
6444 // ---------------------------------------------------------------------------
6445 
6446 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6447 // ensure same repeatnumber
6448 {
6449  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6451 
6452  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6453  {
6454  Utilities->CallLogPop(1024);
6455  return(false);
6456  }
6457  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6458  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6459  {
6460  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6461  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6462  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6463  {
6464  Utilities->CallLogPop(1025);
6465  return(true);
6466  }
6467  }
6468  Utilities->CallLogPop(1026);
6469  return(false);
6470 }
6471 
6472 // ---------------------------------------------------------------------------
6473 
6474 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6475 // ensure same repeatnumber
6476 {
6477  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6478  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6479 
6480  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6481  {
6482  Utilities->CallLogPop(1027);
6483  return(false);
6484  }
6485  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6486  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6487  {
6488  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6489  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6490  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6491  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6492  {
6493  Utilities->CallLogPop(1028);
6494  return(true);
6495  }
6496  }
6497  Utilities->CallLogPop(1029);
6498  return(false);
6499 }
6500 
6501 // ---------------------------------------------------------------------------
6502 
6504 {
6505  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6506  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6507  if(PowerAtRail < 1)
6508  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6509  {
6511  {
6512  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6513  }
6515  Utilities->CallLogPop(2143);
6516  return;
6517  }
6518  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6519 
6521  UnplotTrain(4);
6524  StartSpeed = 0;
6529  HeadCode = NewHeadCode;
6530  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6531  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6532  StoppedAtLocation = true;
6533  PlotStartPosition(6);
6535  // pale green
6538  TerminatedMessageSent = false;
6539  Utilities->CallLogPop(1078);
6540 }
6541 
6542 // ---------------------------------------------------------------------------
6543 
6545 // need to check whether all repeats finished or not
6546 {
6547  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6548  if(RemainHereLogNotSent) // to prevent repeated logs
6549  {
6550  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6551  RemainHereLogNotSent = false;
6552  }
6554  // finished all repeats
6555  {
6557  {
6560  TerminatedMessageSent = true;
6561  // no need to clear message as no more actions
6562  }
6563  TimetableFinished = true;
6564  Utilities->CallLogPop(1080);
6565  return;
6566  }
6567  if(PowerAtRail < 1)
6568  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6569  {
6571  {
6572  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6573  }
6575  Utilities->CallLogPop(2144);
6576  return;
6577  }
6578  int TempRepeatNumber = RepeatNumber + 1;
6579  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6580  // until after LogAction or the wrong time will be used
6581  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6582 
6584  RepeatNumber++;
6585  UnplotTrain(5);
6588  StartSpeed = 0;
6593  HeadCode = NewHeadCode;
6594  StoppedAtLocation = true;
6595  PlotStartPosition(7);
6597  // pale green
6600  TerminatedMessageSent = false;
6601  Utilities->CallLogPop(1079);
6602 }
6603 
6604 // ---------------------------------------------------------------------------
6605 
6607 {
6608  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6609  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6610  if(PowerAtRail < 1)
6611  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6612  {
6614  {
6615  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6616  }
6618  Utilities->CallLogPop(2145);
6619  return;
6620  }
6622  // finished all repeats
6623  {
6624  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6626  RepeatNumber = 0;
6627  UnplotTrain(6);
6630  StartSpeed = 0;
6632  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6633  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6635  HeadCode = NewHeadCode;
6636  StoppedAtLocation = true;
6637  PlotStartPosition(9);
6641  TerminatedMessageSent = false;
6642  Utilities->CallLogPop(1081);
6643  return;
6644  }
6645  int TempRepeatNumber = RepeatNumber + 1;
6646  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6647  // until after LogAction or the wrong time will be used
6648  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6649 
6651  RepeatNumber++;
6652  UnplotTrain(7);
6655  StartSpeed = 0;
6660  HeadCode = NewHeadCode;
6661  StoppedAtLocation = true;
6662  PlotStartPosition(8);
6664  // pale green
6667  TerminatedMessageSent = false;
6668  Utilities->CallLogPop(1082);
6669 }
6670 
6671 // ---------------------------------------------------------------------------
6672 
6674 {
6675  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6676  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6677  // must be preceded by a TimeLoc departure
6678  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6679  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6680  {
6682  {
6683  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6684  {
6685  Utilities->CallLogPop(1083);
6686  return(false);
6687  }
6688  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6689  {
6690  Utilities->CallLogPop(1084);
6691  return(true);
6692  }
6693  }
6694  }
6695  Utilities->CallLogPop(1085);
6696  return(false);
6697 }
6698 
6699 // ---------------------------------------------------------------------------
6700 
6701 bool TTrain::AbleToMove(int Caller)
6702 {
6703  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6704  bool Able = true;
6705 
6706  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0 &
6707  {
6708  // StoppedForTrainInFront removed as tested below
6709  Able = false;
6710  Utilities->CallLogPop(2146); // added v2.4.0
6711  return(Able); // added v2.4.0
6712  }
6713  if(LeadElement > -1)
6714  {
6715  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6716  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6717  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront)
6718  {
6719  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6720  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6721  {
6722  Able = true;
6723  StoppedForTrainInFront = false;
6724  }
6725  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6726  {
6727  Able = true;
6728  StoppedForTrainInFront = false;
6729  }
6730  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6731  {
6732  Able = true;
6733  StoppedForTrainInFront = false;
6734  }
6735  }
6736  else
6737  {
6739  {
6740  Able = false;
6741  }
6742  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6743  }
6744  }
6745  else // leaving at a continuation so keep going
6746  {
6747  Able = true;
6748  StoppedForTrainInFront = false;
6749  }
6750  Utilities->CallLogPop(1454);
6751  return(Able);
6752 }
6753 
6754 // ---------------------------------------------------------------------------
6755 
6757 {
6758  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6759  // won't be set; if there is a train then set StoppedForTrainInFront
6760  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6761  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6762  if(LeadElement == -1) // exiting at continuation
6763  {
6764  Utilities->CallLogPop(2045);
6765  return(false);
6766  }
6767  // end of addition
6768  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6769  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6770 
6771  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6772  {
6773  StoppedForTrainInFront = true;
6774  Utilities->CallLogPop(1455);
6775  return(false);
6776  }
6777  else
6778  {
6779  Utilities->CallLogPop(1456);
6781  // StoppedWithoutPower added v2.4.0
6782  }
6783 }
6784 
6785 // ---------------------------------------------------------------------------
6786 
6788 {
6789  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6790  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6791  TColor TempColour = BackgroundColour;
6792 
6793  UnplotTrain(8);
6796  StartSpeed = 0;
6797  PlotStartPosition(2);
6798  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6799 
6800  //now erase a stub route if there is one, added at v2.5.1
6801  //first element of route is now immediately behind the train (i.e. next to MidElement)
6802  if(MidEntryPos >= 0)
6803  {
6804  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6805  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6806  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6807  int RouteNumber = -1;
6808  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6809  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6810  {
6811  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6812  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6813  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6814  {
6815  bool FirstPass = true; //added at v2.8.0
6816  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a route extends to another signal
6817  {
6818  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6819  int TVPos2 = PDE.GetTrackVectorPosition();
6820  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6821  {
6822  break;
6823  }
6824  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6826  {
6827  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6828  }
6829  else
6830  {
6831  break;
6832  }
6833  FirstPass = false;
6834  }
6835  AllRoutes->RebuildRailwayFlag = true;
6836  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6837  }
6838  }
6839  }
6840  Utilities->CallLogPop(1102);
6841 }
6842 
6843 // ---------------------------------------------------------------------------
6844 
6846 {
6847  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6848  ",FloatingLabelNextString" + "," + HeadCode);
6849  AnsiString RetStr = "", LocationName = "";
6850 
6851  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6852  {
6853  throw Exception("Error - start entry in FloatingLabelNextString");
6854  }
6855  if(Ptr->FormatType == TimeTimeLoc)
6856  {
6857  if(TrainMode == Timetable)
6858  {
6859  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6860  // not arrived yet in tt mode
6861  {
6862  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
6863  }
6864  else
6865  {
6866  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
6867  }
6868  }
6869  else // TrainMode == Signaller
6870  {
6871  if(!DepartureTimeSet) // not arrived yet
6872  {
6873  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
6874  }
6875  else
6876  {
6877  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
6878  }
6879  }
6880  }
6881  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6882  {
6883  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
6884  }
6885  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6886  {
6887  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
6888  }
6889  else if(Ptr->FormatType == PassTime) // new
6890  {
6891  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
6892  }
6893  else if(Ptr->Command == "Fns")
6894  {
6895  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
6896  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
6897  RetStr = CheckNewServiceDepartureTime(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6898  }
6899  else if(Ptr->Command == "F-nshs")
6900  {
6901  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
6903  RetStr = CheckNewServiceDepartureTime(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6904  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6905  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6906  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6907  }
6908  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6909  {
6910  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6911  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
6912  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6913  RetStr = CheckNewServiceDepartureTime(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6914  }
6915  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6916  {
6917  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6918  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
6919  RetStr = CheckNewServiceDepartureTime(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6920  }
6921  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6922  {
6923  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6924  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
6925  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6926  RetStr = CheckNewServiceDepartureTime(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6927  }
6928  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6929  {
6930  RetStr ="None, train terminated at " + Ptr->LocationName;
6931  }
6932  else if(Ptr->Command == "Frh")
6933  {
6934  RetStr = "None, train terminated at " + Ptr->LocationName;
6935  }
6936  else if(Ptr->Command == "Fer")
6937  {
6938  AnsiString AllowedExits = "";
6939  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime)) + AllowedExits;
6940  }
6941  else if(Ptr->Command == "Fjo")
6942  {
6943  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
6945  }
6946  else if(Ptr->Command == "jbo")
6947  {
6948  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6949  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
6950  }
6951  else if(Ptr->Command == "fsp")
6952  {
6953  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6954  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
6955  }
6956  else if(Ptr->Command == "rsp")
6957  {
6958  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6959  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
6960  }
6961  else if(Ptr->Command == "cdt")
6962  {
6963  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
6964  }
6965  Utilities->CallLogPop(1124);
6966  return(RetStr);
6967 }
6968 
6969 // ---------------------------------------------------------------------------
6970 
6971 AnsiString TTrain::CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
6972 {
6973  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
6974  + AnsiString(RptNum) + ",CheckNewServiceDepartureTime," + HeadCode);
6975  AnsiString DepTime = "", EventTime = "";
6976  bool CDTFlag = false; //reports if train changes direction before departs
6977  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
6978  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
6979  {
6980  if(AVI->Command == "cdt")
6981  {
6982  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
6983  continue;
6984  }
6985  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
6986  {
6987  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
6988  RetStr += "\nNew service splits at " + EventTime;
6989  Utilities->CallLogPop(2234);
6990  return(RetStr);
6991  }
6992  if(AVI->Command == "jbo")
6993  {
6994  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
6995  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
6996  Utilities->CallLogPop(2235);
6997  return(RetStr);
6998  }
6999  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7000  {
7001  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7002  if(CDTFlag)
7003  {
7004  RetStr += "\nNew service changes direction then departs at " + DepTime;
7005  }
7006  else
7007  {
7008  RetStr += "\nNew service departs at " + DepTime;
7009  }
7010  Utilities->CallLogPop(2236);
7011  return(RetStr);
7012  }
7013  }
7014  Utilities->CallLogPop(2208);
7015  return(RetStr); //if reach here then RetStr doesn't change
7016 }
7017 
7018 // ---------------------------------------------------------------------------
7019 
7021 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7022 {
7023  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7024  ",FloatingTimetableString" + "," + HeadCode);
7025  AnsiString RetStr = "", PartStr = "";
7026  int Count = 0;
7027 
7028  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7029  // can start in signaller control so exclude this
7030  {
7031  throw Exception("Error - start entry in FloatingTimetableString");
7032  }
7033  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTime Loc check later
7034  bool FirstPass = true;
7035  Ptr--; // because incremented at start of loop
7036 
7037  // different first TimeTimeLoc display if in signaller control
7038  do
7039  {
7040  Ptr++;
7041  if((Ptr->FormatType == Repeat) || TimetableFinished)
7042  {
7043  break;
7044  }
7045  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7046  {
7047  AnsiString TrainLoc = "";
7048  if(TrainMode == Timetable)
7049  {
7050  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7051  {
7052  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7053  }
7054  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7055  {
7056  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7057  }
7058  else
7059  {
7060  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7061  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7062  Count++; // because there are 2 entries
7063  }
7064  }
7065  else // TrainMode == Signaller
7066  {
7067  if(DepartureTimeSet)
7068  {
7069  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7070  }
7071  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7072  {
7073  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7074  }
7075  else
7076  {
7077  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7078  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7079  Count++; // because there are 2 entries
7080  }
7081  }
7082  }
7083  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7084  {
7085  AnsiString TrainLoc = "";
7086  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7087  {
7088  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7089  }
7090  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7091  {
7092  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7093  }
7094  else
7095  {
7096  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7097  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7098  Count++; // because there are 2 entries
7099  }
7100  }
7101  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7102  {
7103  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7104  }
7105  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7106  {
7107  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7108  }
7109  else if(Ptr->FormatType == PassTime) // new
7110  {
7111  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7112  }
7113  else if(Ptr->Command == "Fns")
7114  {
7115  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7116  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7117  PartStr = CheckNewServiceDepartureTime(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
7118  }
7119  else if(Ptr->Command == "F-nshs")
7120  {
7121  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7122  Ptr->LocationName;
7123  PartStr = CheckNewServiceDepartureTime(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7124  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7125  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7126  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7127  }
7128  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7129  {
7130  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7131  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7132  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7133  PartStr = CheckNewServiceDepartureTime(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7134  }
7135  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7136  {
7137  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7138  +" at " + Ptr->LocationName;
7139  PartStr = CheckNewServiceDepartureTime(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7140  }
7141  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7142  {
7143  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7144  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7145  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7146  PartStr = CheckNewServiceDepartureTime(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7147  }
7148  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7149  {
7150  PartStr = "Terminate at " + Ptr->LocationName;
7151  }
7152  else if(Ptr->Command == "Frh")
7153  {
7154  PartStr = "Terminate at " + Ptr->LocationName;
7155  }
7156  else if(Ptr->Command == "Fer")
7157  {
7158  AnsiString AllowedExits = "";
7159  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7160  }
7161  else if(Ptr->Command == "Fjo")
7162  {
7163  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7164  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7165  }
7166  else if(Ptr->Command == "jbo")
7167  {
7168  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7169  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7170  }
7171  else if(Ptr->Command == "fsp")
7172  {
7173  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7174  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7175  }
7176  else if(Ptr->Command == "rsp")
7177  {
7178  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7179  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7180  }
7181  else if(Ptr->Command == "cdt")
7182  {
7183  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7184  }
7185  if(RetStr != "")
7186  {
7187  RetStr = RetStr + '\n' + PartStr;
7188  }
7189  else
7190  {
7191  RetStr = PartStr;
7192  }
7193  FirstPass = false;
7194  Count++;
7195  }
7196  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7197  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7198  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7199  // forward as anyone should wish to see without looking at the full timetable
7200  if(TimetableFinished)
7201  {
7202  if(TrainMode == Timetable)
7203  {
7204  RetStr = "Timetable finished";
7205  }
7206  else
7207  {
7208  RetStr = "No timetable";
7209  }
7210  }
7211  Utilities->CallLogPop(1125);
7212  return(RetStr);
7213 }
7214 
7215 // ---------------------------------------------------------------------------
7216 
7217 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7218 {
7219  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7220  Utilities->SaveFileString(OutFile, HeadCode);
7223  Utilities->SaveFileInt(OutFile, StartSpeed);
7226  Utilities->SaveFileInt(OutFile, RepeatNumber);
7229  Utilities->SaveFileInt(OutFile, Mass);
7232  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7239  Utilities->SaveFileDouble(OutFile, BrakeRate);
7243  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7244  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7245  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7246  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7247  Utilities->SaveFileDouble(OutFile, double(TRSTime));
7248  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
7252  Utilities->SaveFileInt(OutFile, (short)TrainMode);
7257  Utilities->SaveFileBool(OutFile, Derailed);
7259  Utilities->SaveFileBool(OutFile, Crashed);
7266  Utilities->SaveFileBool(OutFile, NotInService);
7267  Utilities->SaveFileBool(OutFile, Plotted);
7268  Utilities->SaveFileBool(OutFile, TrainGone);
7269  Utilities->SaveFileBool(OutFile, SPADFlag);
7271  Utilities->SaveFileInt(OutFile, HOffset[0]);
7272  Utilities->SaveFileInt(OutFile, HOffset[1]);
7273  Utilities->SaveFileInt(OutFile, HOffset[2]);
7274  Utilities->SaveFileInt(OutFile, HOffset[3]);
7275  Utilities->SaveFileInt(OutFile, VOffset[0]);
7276  Utilities->SaveFileInt(OutFile, VOffset[1]);
7277  Utilities->SaveFileInt(OutFile, VOffset[2]);
7278  Utilities->SaveFileInt(OutFile, VOffset[3]);
7279  Utilities->SaveFileInt(OutFile, PlotElement[0]);
7280  Utilities->SaveFileInt(OutFile, PlotElement[1]);
7281  Utilities->SaveFileInt(OutFile, PlotElement[2]);
7282  Utilities->SaveFileInt(OutFile, PlotElement[3]);
7283  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
7284  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
7285  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
7286  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
7288  Utilities->SaveFileInt(OutFile, (short)Straddle);
7289  Utilities->SaveFileInt(OutFile, NextTrainID);
7290  Utilities->SaveFileInt(OutFile, TrainID);
7291  Utilities->SaveFileInt(OutFile, LeadElement);
7292  Utilities->SaveFileInt(OutFile, LeadEntryPos);
7293  Utilities->SaveFileInt(OutFile, LeadExitPos);
7294  Utilities->SaveFileInt(OutFile, MidElement);
7295  Utilities->SaveFileInt(OutFile, MidEntryPos);
7296  Utilities->SaveFileInt(OutFile, MidExitPos);
7297  Utilities->SaveFileInt(OutFile, LagElement);
7298  Utilities->SaveFileInt(OutFile, LagEntryPos);
7299  Utilities->SaveFileInt(OutFile, LagExitPos);
7300  int ColourNumber;
7301 
7303  {
7304  ColourNumber = 0;
7305  }
7307  {
7308  ColourNumber = 1;
7309  }
7311  {
7312  ColourNumber = 2;
7313  }
7315  {
7316  ColourNumber = 3;
7317  }
7319  {
7320  ColourNumber = 4;
7321  }
7323  {
7324  ColourNumber = 5;
7325  }
7327  {
7328  ColourNumber = 6;
7329  }
7331  {
7332  ColourNumber = 7;
7333  }
7335  {
7336  ColourNumber = 8;
7337  }
7339  {
7340  ColourNumber = 9;
7341  }
7343  {
7344  ColourNumber = 10;
7345  }
7347  {
7348  ColourNumber = 11;
7349  }
7351  {
7352  ColourNumber = 12;
7353  }
7354  else if(BackgroundColour == clTRSBackground)
7355  {
7356  ColourNumber = 13;
7357  }
7359  {
7360  ColourNumber = 14; // added at v2.4.0
7361  }
7362  Utilities->SaveFileInt(OutFile, ColourNumber);
7363 
7364  // additional data
7365  bool ForwardHeadCode;
7366 
7367  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
7368  {
7369  ForwardHeadCode = true;
7370  }
7371  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
7372  else
7373  {
7374  ForwardHeadCode = false;
7375  }
7376  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
7377 
7378  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
7379 
7380  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
7381  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
7382 
7383  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
7384  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
7385  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
7386  // so use the last asterisk position for this - 0 for false & 1 for true
7387  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
7388  AnsiString Marker;
7389 
7391  {
7392  Marker = "*****1";
7393  }
7394  else
7395  {
7396  Marker = "*****0";
7397  }
7398  if(RestoreTimetableLocation == "")
7399  {
7400  Utilities->SaveFileString(OutFile, Marker);
7401  }
7402  else
7403  {
7404  AnsiString CombinedString = RestoreTimetableLocation + Marker;
7405  Utilities->SaveFileString(OutFile, CombinedString);
7406  // RestoreTimetableLocation + marker
7407  }
7408  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
7409  Utilities->CallLogPop(1457);
7410 }
7411 
7412 // ---------------------------------------------------------------------------
7413 
7414 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
7415 {
7416  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
7417  HeadCode = Utilities->LoadFileString(InFile);
7420  StartSpeed = Utilities->LoadFileInt(InFile);
7422  if(SignallerMaxSpeed < 10)
7423  {
7424  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
7425  }
7427  RepeatNumber = Utilities->LoadFileInt(InFile);
7430  Mass = Utilities->LoadFileInt(InFile);
7433  {
7435  }
7436  // above added at v2.1.0 for legacy session files where value may not have been limited
7438  EntrySpeed = Utilities->LoadFileDouble(InFile);
7442  if(TimetableMaxRunningSpeed < 10)
7443  {
7444  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7445  }
7447  if(MaxRunningSpeed < 10)
7448  {
7449  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7450  }
7453  BrakeRate = Utilities->LoadFileDouble(InFile);
7457  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
7458  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
7459  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
7460  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
7461  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
7462  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
7471  Derailed = Utilities->LoadFileBool(InFile);
7473  Crashed = Utilities->LoadFileBool(InFile);
7480  NotInService = Utilities->LoadFileBool(InFile);
7481  Plotted = Utilities->LoadFileBool(InFile);
7482  TrainGone = Utilities->LoadFileBool(InFile);
7483  SPADFlag = Utilities->LoadFileBool(InFile);
7485  HOffset[0] = Utilities->LoadFileInt(InFile);
7486  HOffset[1] = Utilities->LoadFileInt(InFile);
7487  HOffset[2] = Utilities->LoadFileInt(InFile);
7488  HOffset[3] = Utilities->LoadFileInt(InFile);
7489  VOffset[0] = Utilities->LoadFileInt(InFile);
7490  VOffset[1] = Utilities->LoadFileInt(InFile);
7491  VOffset[2] = Utilities->LoadFileInt(InFile);
7492  VOffset[3] = Utilities->LoadFileInt(InFile);
7493  PlotElement[0] = Utilities->LoadFileInt(InFile);
7494  PlotElement[1] = Utilities->LoadFileInt(InFile);
7495  PlotElement[2] = Utilities->LoadFileInt(InFile);
7496  PlotElement[3] = Utilities->LoadFileInt(InFile);
7497  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7498  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7499  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7500  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7502  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7503  NextTrainID = Utilities->LoadFileInt(InFile);
7504  // will be same for all but best to save all anyway
7505  TrainID = Utilities->LoadFileInt(InFile);
7506  LeadElement = Utilities->LoadFileInt(InFile);
7507  LeadEntryPos = Utilities->LoadFileInt(InFile);
7508  LeadExitPos = Utilities->LoadFileInt(InFile);
7509  MidElement = Utilities->LoadFileInt(InFile);
7510  MidEntryPos = Utilities->LoadFileInt(InFile);
7511  MidExitPos = Utilities->LoadFileInt(InFile);
7512  LagElement = Utilities->LoadFileInt(InFile);
7513  LagEntryPos = Utilities->LoadFileInt(InFile);
7514  LagExitPos = Utilities->LoadFileInt(InFile);
7515  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7516 
7517  if(ColourNumber == 0)
7518  {
7520  }
7521  else if(ColourNumber == 1)
7522  {
7524  }
7525  else if(ColourNumber == 2)
7526  {
7528  }
7529  else if(ColourNumber == 3)
7530  {
7532  }
7533  else if(ColourNumber == 4)
7534  {
7536  }
7537  else if(ColourNumber == 5)
7538  {
7540  }
7541  else if(ColourNumber == 6)
7542  {
7544  }
7545  else if(ColourNumber == 7)
7546  {
7548  }
7549  else if(ColourNumber == 8)
7550  {
7552  }
7553  else if(ColourNumber == 9)
7554  {
7556  }
7557  else if(ColourNumber == 10)
7558  {
7560  }
7561  else if(ColourNumber == 11)
7562  {
7564  }
7565  else if(ColourNumber == 12)
7566  {
7568  }
7569  else if(ColourNumber == 13)
7570  {
7572  }
7573  else if(ColourNumber == 14)
7574  {
7575  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7576 
7577  }
7578  // additional data
7580  // sets the BackgroundColour to the loaded value
7581  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7582 
7583  if(ForwardHeadCode)
7584  {
7585  for(int x = 0; x < 4; x++)
7586  {
7588  }
7589  }
7590  else
7591  {
7592  for(int x = 0; x < 4; x++)
7593  {
7594  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7595  }
7596  }
7597  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7598  if(TrainMode == Timetable)
7599  {
7600  if(Crashed)
7601  {
7603  }
7604  else
7605  {
7607  }
7608  }
7609  else
7610  {
7612  }
7614  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7615  if(Straddle == LeadMid)
7616  {
7617  if(LeadElement > -1)
7618  {
7620  }
7621  if(LeadElement > -1)
7622  {
7624  }
7625  if(MidElement > -1)
7626  {
7628  }
7629  if(MidElement > -1)
7630  {
7632  }
7633  }
7634  else if(Straddle == LeadMidLag)
7635  {
7636  if(LeadElement > -1)
7637  {
7639  }
7640  if(MidElement > -1)
7641  {
7643  }
7644  if(MidElement > -1)
7645  {
7647  }
7648  if(LagElement > -1)
7649  {
7651  }
7652  }
7653  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7654 
7655  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7656  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7657 
7658  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7659 
7660  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7661  if(LeadElement > -1)
7662  // need to include this in case train exiting & no lead element
7663  {
7665  {
7666  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7667  }
7668  }
7669  AValue = sqrt(2 * PowerAtRail / Mass);
7670 
7671  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7672 
7673  // possible RestoreTimetableLocation + Marker, where Marker is
7674  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7675  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7676  // added at beta v0.2e
7677  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7678  // name not allowed to include the '*' character
7679  {
7680  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7681  bool GiveMessagesFalse = false;
7682  bool CheckLocationsExistInRailwayTrue = true;
7683  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7684  {
7685  // otherwise take no action
7686  RestoreTimetableLocation = Location;
7687  }
7688  }
7689  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7690 
7691  StoppedWithoutPower = false;
7692  if(Marker[6] == '1')
7693  {
7694  StoppedWithoutPower = true;
7695  }
7696  Utilities->CallLogPop(1458);
7697 }
7698 
7699 // ---------------------------------------------------------------------------
7700 
7701 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7702 {
7704  {
7705  return(false); // HeadCode
7706 
7707  }
7708  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7709  {
7710  return(false); // RearStartElement
7711 
7712  }
7713  if(!Utilities->CheckFileInt(InFile, 0, 3))
7714  {
7715  return(false); // RearStartExitPos
7716 
7717  }
7718  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7719  {
7720  return(false); // StartSpeed
7721 
7722  }
7723  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7724  {
7725  return(false); // SignallerMaxSpeed
7726 
7727  }
7728  if(!Utilities->CheckFileBool(InFile))
7729  {
7730  return(false); // HoldAtLocationInTTMode
7731 
7732  }
7733  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7734  {
7735  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
7736 
7737  }
7738  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7739  {
7740  return(false); // IncrementalMinutes (max 96 x 60)
7741 
7742  }
7743  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7744  {
7745  return(false); // IncrementalDigits
7746 
7747  }
7748  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7749  {
7750  return(false); // Mass
7751 
7752  }
7753  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7754  {
7755  return(false);
7756  }
7757  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7758  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7759  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7760  {
7761  return(false); // FrontElementLength
7762 
7763  }
7764  if(!Utilities->CheckFileDouble(InFile))
7765  {
7766  return(false); // EntrySpeed
7767 
7768  }
7769  if(!Utilities->CheckFileDouble(InFile))
7770  {
7771  return(false); // ExitSpeedHalf
7772 
7773  }
7774  if(!Utilities->CheckFileDouble(InFile))
7775  {
7776  return(false); // ExitSpeedFull
7777 
7778  }
7779  if(!Utilities->CheckFileDouble(InFile))
7780  {
7781  return(false); // TimetableMaxRunningSpeed
7782 
7783  }
7784  if(!Utilities->CheckFileDouble(InFile))
7785  {
7786  return(false); // MaxRunningSpeed
7787 
7788  }
7789  if(!Utilities->CheckFileDouble(InFile))
7790  {
7791  return(false); // MaxExitSpeed
7792 
7793  }
7794  if(!Utilities->CheckFileDouble(InFile))
7795  {
7796  return(false); // MaxBrakeRate
7797 
7798  }
7799  if(!Utilities->CheckFileDouble(InFile))
7800  {
7801  return(false); // BrakeRate
7802 
7803  }
7804  if(!Utilities->CheckFileDouble(InFile))
7805  {
7806  return(false); // PowerAtRail
7807 
7808  }
7809  if(!Utilities->CheckFileBool(InFile))
7810  {
7811  return(false); // FirstHalfMove
7812 
7813  }
7814  if(!Utilities->CheckFileBool(InFile))
7815  {
7816  return(false); // OneLengthAccelDecel
7817 
7818  }
7819  if(!Utilities->CheckFileDouble(InFile))
7820  {
7821  return(false); // double(EntryTime)
7822 
7823  }
7824  if(!Utilities->CheckFileDouble(InFile))
7825  {
7826  return(false); // double(ExitTimeHalf)
7827 
7828  }
7829  if(!Utilities->CheckFileDouble(InFile))
7830  {
7831  return(false); // double(ExitTimeFull)
7832 
7833  }
7834  if(!Utilities->CheckFileDouble(InFile))
7835  {
7836  return(false); // double(ReleaseTime)
7837 
7838  }
7839  if(!Utilities->CheckFileDouble(InFile))
7840  {
7841  return(false); // double(TRSTime)
7842 
7843  }
7844  if(!Utilities->CheckFileDouble(InFile))
7845  {
7846  return(false); // double(LastActionTime)
7847 
7848  }
7849  if(!Utilities->CheckFileBool(InFile))
7850  {
7851  return(false); // CallingOnFlag
7852 
7853  }
7854  if(!Utilities->CheckFileBool(InFile))
7855  {
7856  return(false); // BeingCalledOn
7857 
7858  }
7859  if(!Utilities->CheckFileBool(InFile))
7860  {
7861  return(false); // DepartureTimeSet
7862 
7863  }
7864  if(!Utilities->CheckFileInt(InFile, 0, 2))
7865  {
7866  return(false); // (short)TrainMode
7867 
7868  }
7869  if(!Utilities->CheckFileBool(InFile))
7870  {
7871  return(false); // TimetableFinished
7872 
7873  }
7874  if(!Utilities->CheckFileBool(InFile))
7875  {
7876  return(false); // LastActionDelayFlag
7877 
7878  }
7879  if(!Utilities->CheckFileBool(InFile))
7880  {
7881  return(false); // SignallerRemoved
7882 
7883  }
7884  if(!Utilities->CheckFileBool(InFile))
7885  {
7886  return(false); // TerminatedMessageSent
7887 
7888  }
7889  if(!Utilities->CheckFileBool(InFile))
7890  {
7891  return(false); // Derailed
7892 
7893  }
7894  if(!Utilities->CheckFileBool(InFile))
7895  {
7896  return(false); // DerailPending
7897 
7898  }
7899  if(!Utilities->CheckFileBool(InFile))
7900  {
7901  return(false); // Crashed
7902 
7903  }
7904  if(!Utilities->CheckFileBool(InFile))
7905  {
7906  return(false); // StoppedAtBuffers
7907 
7908  }
7909  if(!Utilities->CheckFileBool(InFile))
7910  {
7911  return(false); // StoppedAtSignal
7912 
7913  }
7914  if(!Utilities->CheckFileBool(InFile))
7915  {
7916  return(false); // StoppedAtLocation
7917 
7918  }
7919  if(!Utilities->CheckFileBool(InFile))
7920  {
7921  return(false); // SignallerStopped
7922 
7923  }
7924  if(!Utilities->CheckFileBool(InFile))
7925  {
7926  return(false); // StoppedAfterSPAD
7927 
7928  }
7929  if(!Utilities->CheckFileBool(InFile))
7930  {
7931  return(false); // StoppedForTrainInFront
7932 
7933  }
7934  if(!Utilities->CheckFileBool(InFile))
7935  {
7936  return(false); // NotInService
7937 
7938  }
7939  if(!Utilities->CheckFileBool(InFile))
7940  {
7941  return(false); // Plotted
7942 
7943  }
7944  if(!Utilities->CheckFileBool(InFile))
7945  {
7946  return(false); // TrainGone
7947 
7948  }
7949  if(!Utilities->CheckFileBool(InFile))
7950  {
7951  return(false); // SPADFlag
7952 
7953  }
7954  if(!Utilities->CheckFileBool(InFile))
7955  {
7956  return(false); // TimeTimeLocArrived
7957 
7958  }
7959  if(!Utilities->CheckFileInt(InFile, 0, 15))
7960  {
7961  return(false); // HOffset[0]
7962 
7963  }
7964  if(!Utilities->CheckFileInt(InFile, 0, 15))
7965  {
7966  return(false); // HOffset[1]
7967 
7968  }
7969  if(!Utilities->CheckFileInt(InFile, 0, 15))
7970  {
7971  return(false); // HOffset[2]
7972 
7973  }
7974  if(!Utilities->CheckFileInt(InFile, 0, 15))
7975  {
7976  return(false); // HOffset[3]
7977 
7978  }
7979  if(!Utilities->CheckFileInt(InFile, 0, 15))
7980  {
7981  return(false); // VOffset[0]
7982 
7983  }
7984  if(!Utilities->CheckFileInt(InFile, 0, 15))
7985  {
7986  return(false); // VOffset[1]
7987 
7988  }
7989  if(!Utilities->CheckFileInt(InFile, 0, 15))
7990  {
7991  return(false); // VOffset[2]
7992 
7993  }
7994  if(!Utilities->CheckFileInt(InFile, 0, 15))
7995  {
7996  return(false); // VOffset[3]
7997 
7998  }
7999  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8000  {
8001  return(false); // PlotElement[0]
8002 
8003  }
8004  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8005  {
8006  return(false); // PlotElement[1]
8007 
8008  }
8009  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8010  {
8011  return(false); // PlotElement[2]
8012 
8013  }
8014  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8015  {
8016  return(false); // PlotElement[3]
8017 
8018  }
8019  if(!Utilities->CheckFileInt(InFile, 0, 3))
8020  {
8021  return(false); // PlotEntryPos[0]
8022 
8023  }
8024  if(!Utilities->CheckFileInt(InFile, 0, 3))
8025  {
8026  return(false); // PlotEntryPos[1]
8027 
8028  }
8029  if(!Utilities->CheckFileInt(InFile, 0, 3))
8030  {
8031  return(false); // PlotEntryPos[2]
8032 
8033  }
8034  if(!Utilities->CheckFileInt(InFile, 0, 3))
8035  {
8036  return(false); // PlotEntryPos[3]
8037 
8038  }
8039  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8040  {
8041  return(false); // TrainCrashedInto
8042 
8043  }
8044  if(!Utilities->CheckFileInt(InFile, 0, 2))
8045  {
8046  return(false); // (short)Straddle
8047 
8048  }
8049  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8050  {
8051  return(false); // NextTrainID
8052 
8053  }
8054  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8055  {
8056  return(false); // TrainID
8057 
8058  }
8059  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8060  {
8061  return(false); // LeadElement
8062 
8063  }
8064  if(!Utilities->CheckFileInt(InFile, 0, 3))
8065  {
8066  return(false); // LeadEntryPos
8067 
8068  }
8069  if(!Utilities->CheckFileInt(InFile, 0, 3))
8070  {
8071  return(false); // LeadExitPos
8072 
8073  }
8074  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8075  {
8076  return(false); // MidElement
8077 
8078  }
8079  if(!Utilities->CheckFileInt(InFile, 0, 3))
8080  {
8081  return(false); // MidEntryPos
8082 
8083  }
8084  if(!Utilities->CheckFileInt(InFile, 0, 3))
8085  {
8086  return(false); // MidExitPos
8087 
8088  }
8089  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8090  {
8091  return(false); // LagElement
8092 
8093  }
8094  if(!Utilities->CheckFileInt(InFile, 0, 3))
8095  {
8096  return(false); // LagEntryPos
8097 
8098  }
8099  if(!Utilities->CheckFileInt(InFile, 0, 3))
8100  {
8101  return(false); // LagExitPos
8102 
8103  }
8104  if(!Utilities->CheckFileInt(InFile, 0, 14))
8105  {
8106  return(false);
8107  }
8108  // Background colour number //14 is failed colour at v2.4.0
8109  if(!Utilities->CheckFileBool(InFile))
8110  {
8111  return(false); // ForwardHeadCode
8112 
8113  }
8114  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8115  {
8116  return(false); // TrainDataEntryValue
8117 
8118  }
8119  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8120  {
8121  return(false); // ActionVectorEntryValue
8122 
8123  }
8125  {
8126  return(false); // End of train marker + possible RestoreTimetableLocation
8127 
8128  }
8129  // and StoppedWithoutPower flag
8130  return(true);
8131 }
8132 
8133 // ---------------------------------------------------------------------------
8134 
8135 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8136 {
8137  // order below reflects significance so earlier shows first, as may have more than one flag set
8138  // only plot flashing trains when Flash is true
8139 
8140 /*
8141  clCrashedBackground (TColor)0x0000FF red
8142  clDerailedBackground (TColor)0x0000FF red
8143  clSPADBackground (TColor)0x00FFFF yellow
8144  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8145  clCallOnBackground (TColor)0xFF33FF light magenta
8146  clSignalStopBackground (TColor)0x00FF66 green
8147  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8148  clStationStopBackground (TColor)0xCCFFCC pale green
8149  clTRSBackground (TColor)0xFFCCFF light pink
8150  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8151  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8152  clSignallerStopped (TColor)0x99CCFF caramel
8153  clNormalBackground (TColor)0xCCCCCC grey
8154 */
8155 
8156  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8157  bool HideFlashingTrain = true;
8158  // hide it when Flash false so it blinks on and off
8159  // if don't hide it it stays displayed all the time
8160  Graphics::TBitmap *SmallTrainBitmap;
8161 
8162  // NB ensure retain same order as zoomed in order so colours correspond
8164  {
8165  TrainController->CrashWarning = true;
8166  SmallTrainBitmap = RailGraphics->smRed;
8167  }
8169  {
8171  SmallTrainBitmap = RailGraphics->smRed;
8172  }
8174  {
8175  TrainController->SPADWarning = true;
8176  SmallTrainBitmap = RailGraphics->smYellow;
8177  }
8179  {
8181  SmallTrainBitmap = RailGraphics->smOrange;
8182  }
8184  {
8186  SmallTrainBitmap = RailGraphics->smMagenta;
8187  }
8189  {
8191  SmallTrainBitmap = RailGraphics->smBrightGreen;
8192  }
8194  {
8196  SmallTrainBitmap = RailGraphics->smCyan;
8197  }
8199  {
8200  SmallTrainBitmap = RailGraphics->smPaleGreen;
8201  HideFlashingTrain = false;
8202  }
8204  {
8205  SmallTrainBitmap = RailGraphics->smCyan;
8206  HideFlashingTrain = false;
8207  }
8209  {
8210  SmallTrainBitmap = RailGraphics->smLightBlue;
8211  HideFlashingTrain = false;
8212  }
8214  {
8215  SmallTrainBitmap = RailGraphics->smCaramel;
8216  HideFlashingTrain = false;
8217  }
8218  else
8219  {
8220  SmallTrainBitmap = RailGraphics->smBlack; // moving
8221  HideFlashingTrain = false;
8222  }
8223  // now plot the new train
8224  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8225  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8226  {
8227  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8228  }
8229  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8230  {
8231  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8232  }
8233  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8234  {
8235  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8236  }
8240  Utilities->CallLogPop(1459);
8241 }
8242 
8243 // ---------------------------------------------------------------------------
8244 
8246 {
8247  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
8248  if(!Display->ZoomOutFlag)
8249  {
8250  Utilities->CallLogPop(1304);
8251  return;
8252  }
8253  for(int y = 0; y < 3; y++)
8254  {
8255  if(OldZoomOutElement[y] > -1)
8256  {
8257  bool FoundFlag = false;
8258  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
8259  TTrackElement IATElement1, IATElement2;
8260  // default elements to begin with
8261  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
8262  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
8263  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
8264  if(FoundFlag)
8265  {
8266  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
8267  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
8268  if(IMPair.first != IMPair.second)
8269  {
8270  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
8271  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
8272  }
8273  }
8274  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
8275  }
8276  }
8277  Utilities->CallLogPop(1305);
8278 }
8279 
8280 // ---------------------------------------------------------------------------
8281 
8282 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
8283 {
8284  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
8285  LocationName = "";
8286  if(!StoppedAtLocation)
8287  {
8288  Utilities->CallLogPop(1398);
8289  return(false);
8290  }
8291  if(LeadElement > -1)
8292  {
8294  }
8295  if((LocationName == "") && (MidElement > -1))
8296  {
8297  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
8298  }
8299  if((LocationName == "") && (LagElement > -1))
8300  {
8301  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
8302  }
8303  if(LocationName == "")
8304  {
8305  throw Exception("Error - Location name not set in TrainAtLocation");
8306  }
8307  Utilities->CallLogPop(1399);
8308  return(true);
8309 }
8310 
8311 // ---------------------------------------------------------------------------
8312 
8313 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
8314 {
8315  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
8316  for(int x = 0; x < 4; x++)
8317  {
8318  PlotTrainGraphic(7, x, Disp);
8319  }
8320  Utilities->CallLogPop(647);
8321 }
8322 
8323 // ---------------------------------------------------------------------------
8324 
8325 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
8326 {
8327  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
8328  for(int x = 0; x < 4; x++)
8329  {
8330  if(PlotElement[x] > -1)
8331  {
8332  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
8333  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
8334  }
8335  }
8336  Utilities->CallLogPop(1708);
8337 }
8338 
8339 // ---------------------------------------------------------------------------
8340 
8341 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
8342 {
8343  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
8344  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
8345  AnsiString(LinkNumber) + "," + HeadCode);
8346 
8347 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
8348  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
8349  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
8350 */
8351 
8352  // note that MidElement always fully occupied
8353  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
8354  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
8355  {
8356  Utilities->CallLogPop(2005);
8357  return(true);
8358  }
8359  if(Straddle == LeadMid)
8360  {
8361  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
8362  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
8363  {
8364  Utilities->CallLogPop(2006);
8365  return(true);
8366  }
8367  }
8368  else if(Straddle == LeadMidLag)
8369  {
8370  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
8371  // only interested in LeadEntryPos as train not occupying ExitPos yet
8372  {
8373  Utilities->CallLogPop(2007);
8374  return(true);
8375  }
8376  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
8377  // only interested in LagExitPos as train has left EntryPos
8378  {
8379  Utilities->CallLogPop(2008);
8380  return(true);
8381  }
8382  }
8383  Utilities->CallLogPop(2009);
8384  return(false);
8385 }
8386 
8387 // ---------------------------------------------------------------------------
8388 
8389 float TTrain::CalcTimeToAct(int Caller) // only called for running trains
8394 {
8395  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
8396  int DistanceToRedSignal = 0;
8397  float TimeToAct = 0;
8398  float MinsEarly = 0; //added at v2.6.1
8399  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
8400  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
8401 
8402  if(TrainFailed)
8403  {
8404  Utilities->CallLogPop(2147);
8405  return(0); // time to act now
8406  }
8407  if(SignallerStopped)
8408  {
8409  Utilities->CallLogPop(2080);
8410  return(-1);
8411  }
8412  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
8413  {
8414  Utilities->CallLogPop(2266);
8415  return(-1);
8416  }
8417  if(!Stopped() || StoppedAtLocation)
8418  {
8419  // calc distance to next red signal but check for continuation exit
8420  if(LeadElement == -1)
8421  // if -1 it's on an end element so no action needed
8422  {
8423  Utilities->CallLogPop(2075);
8424  return(-1);
8425  }
8426  else
8427  {
8428  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
8429  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
8430  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
8431 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
8432  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
8433  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
8434  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
8435  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
8436  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
8437  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
8438  before the train has stopped the current station is still recognised as a future stop.
8439  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
8440  becomes the signal, and the time to act indication becomes 'NOW'.
8441 */
8442  {
8443  FirstPosToBeMeasured = LeadElement;
8444  FirstEntryPos = LeadEntryPos;
8445  }
8446  float CurrentStopTime; // set to 0 at start of function
8447  float LaterStopTime; // set to 0 at start of function
8448  float RecoverableTime; // set to 0 at start of function
8449  int AvTrackSpeed; // set to zero at start of function
8450  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
8451  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
8452  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
8453  if(DistanceToRedSignal == -1) // -1 for no action needed
8454  {
8455  Utilities->CallLogPop(2076);
8456  return(-1);
8457  }
8458 /* Have MinsDelayed; pos or neg,
8459  CurrentStopTime; pos or zero
8460  LaterStopTime; pos or zero
8461  RecoverableTime; pos or zero
8462 
8463  & from these calculate TotalStopTime. noting that:
8464  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
8465  RecoverableTime always < LaterStopTime or both zero
8466  can't subtract more than RecoverableTime (MinsDelayed > 0)
8467  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
8468  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
8469  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
8470  if running early & stopped at location CurrentStopTime will automatically include the excess
8471 */
8472  float TimeToSubtract, TotalStopTime;
8473  if(MinsDelayed > RecoverableTime)
8474  {
8475  TimeToSubtract = RecoverableTime;
8476  }
8477  else
8478  {
8479  TimeToSubtract = MinsDelayed; // may be negative;
8480 
8481  }
8482  if((AvTrackSpeed > 0) && (DistanceToStationStop <= DistanceToRedSignal) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
8483  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
8484  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
8485  //next station stop
8486  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
8487  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
8488  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
8489  //first find departure time from the next stop
8490  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
8491  {
8492  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
8493  {
8496  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8497  }
8498  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
8499  {
8501  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8502  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
8503  {
8504  // must be a departure
8505  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
8506  }
8507  }
8508  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
8509  {
8510  MinsEarly = 0;
8511  }
8512  if(MinsEarly < 0)
8513  {
8514  MinsEarly = 0;
8515  }
8516  }
8517  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
8518  {
8519  if(CurrentStopTime > 0)
8520  {
8521  TotalStopTime = CurrentStopTime + LaterStopTime;
8522  }
8523  // stopped at loc, will depart on time
8524  else
8525  {
8526  TotalStopTime = LaterStopTime - MinsDelayed;
8527  }
8528  // not stopped, will depart on time at first later stop so add the delay
8529  }
8530  else if((MinsEarly > 0) && !Stopped()) //running early
8531  {
8532  TotalStopTime = LaterStopTime + MinsEarly;
8533  }
8534  else // on time or running late
8535  {
8536  if(LaterStopTime == 0)
8537  {
8538  TotalStopTime = CurrentStopTime;
8539  }
8540  // no later stops, if stopped now will depart as soon as possible,
8541  // if not stopped no stop times to add
8542  else
8543  {
8544  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
8545  }
8546  }
8547  if(AvTrackSpeed < 30)
8548  {
8549  AvTrackSpeed = 30;
8550  }
8551  int Speed = AvTrackSpeed;
8552  if(AvTrackSpeed > int(MaxRunningSpeed))
8553  {
8554  Speed = int(MaxRunningSpeed);
8555  }
8556  if(TrainMode == Signaller)
8557  {
8558  Speed = SignallerMaxSpeed;
8559  TotalStopTime = 0;
8560  }
8561  TimeToAct = TotalStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
8562  // accel & decel taken into account in
8563  // CalcDistanceToRedSignalandStopTime
8564  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8565  Utilities->CallLogPop(2079);
8566  return(TimeToAct);
8567  }
8568  }
8569  else // stopped not at location
8570  {
8572  {
8573  TimeToAct = 0.0;
8574  }
8575  // but if stopped at a signal & autosigs route after it then ok
8576  if(StoppedAtSignal)
8577  {
8578  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
8579  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
8580  int NextExitPos;
8581  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
8582  {
8583  if((NextEntryPos == 0) || (NextEntryPos == 2))
8584  // leading entry point
8585  {
8586  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
8587  {
8588  NextExitPos = 1;
8589  }
8590  else
8591  {
8592  NextExitPos = 3;
8593  }
8594  }
8595  else
8596  {
8597  NextExitPos = 0; // trailing entry point
8598  }
8599  }
8600  else
8601  {
8602  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
8603  }
8604  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
8605  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
8606  int RouteNumber; // holder for referenced value, not used
8607  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8608  {
8609  TimeToAct = -1;
8610  }
8611  }
8612  Utilities->CallLogPop(2074);
8613  return(TimeToAct);
8614  }
8615 }
8616 
8617 // ---------------------------------------------------------------------------
8618 
8620 {
8621  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
8622  if(LeadElement > -1)
8623  {
8625  {
8626  Utilities->CallLogPop(2148);
8627  return(true);
8628  }
8629  }
8630  if(MidElement > -1)
8631  {
8633  {
8634  Utilities->CallLogPop(2149);
8635  return(true);
8636  }
8637  }
8638  if(LagElement > -1)
8639  {
8641  {
8642  Utilities->CallLogPop(2150);
8643  return(true);
8644  }
8645  }
8646  Utilities->CallLogPop(2151);
8647  return(false);
8648 }
8649 
8650 // ---------------------------------------------------------------------------
8651 // TTrainController
8652 // ---------------------------------------------------------------------------
8653 
8655 {
8656  OnTimeArrivals = 0;
8657  LateArrivals = 0;
8658  EarlyArrivals = 0;
8659  OnTimePasses = 0;
8660  LatePasses = 0;
8661  EarlyPasses = 0;
8662  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
8663  LateExits = 0;
8664  EarlyExits = 0;
8665  OnTimeDeps = 0;
8666  LateDeps = 0;
8667  MissedStops = 0;
8668  OtherMissedEvents = 0;
8669  UnexpectedExits = 0;
8670  NumFailures = 0;
8671  IncorrectExits = 0;
8672  SPADEvents = 0;
8673  SPADRisks = 0;
8674  CrashedTrains = 0;
8675  Derailments = 0;
8676  TotArrDepPass = 0;
8677  TotLateArrMins = 0;
8678  TotEarlyArrMins = 0;
8679  TotLatePassMins = 0;
8680  TotEarlyPassMins = 0;
8681  TotLateExitMins = 0; //added at v2.9.1
8682  TotEarlyExitMins = 0; //added at v2.9.1
8683  TotLateDepMins = 0;
8684  ExcessLCDownMins = 0;
8685  TTClockTime = 0; // added for v0.6
8687  // added at v1.3.0 to ensure false at start
8688  OpTimeToActUpdateCounter = 0; // new v2.2.0
8689  OpActionPanelVisible = false; // new v2.2.0
8690  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
8691  SSHigh = false;
8692  MRSHigh = false;
8693  MRSLow = false;
8694  MassHigh = false;
8695  BFHigh = false;
8696  BFLow = false;
8697  PwrHigh = false;
8698  SigSHigh = false;
8699  SigSLow = false;
8700  randomize();
8701  // to seed rand() & random() with a random number (see UpdateTrain)
8702 }
8703 
8704 // ---------------------------------------------------------------------------
8705 
8707 {
8708  for(unsigned int x = 0; x < TrainVector.size(); x++)
8709  {
8710  TrainVectorAt(32, x).DeleteTrain(4);
8711  }
8712  TrainVector.clear();
8713 }
8714 
8715 // ---------------------------------------------------------------------------
8716 
8717 void TTrainController::LogEvent(AnsiString Str)
8718 {
8719  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
8720 
8721  // restrict to last 1000 entries
8722  Utilities->EventLog.push_back(FullStr);
8723  if(Utilities->EventLog.size() > 1000)
8724  {
8725  Utilities->EventLog.pop_front();
8726  }
8727 }
8728 
8729 // ---------------------------------------------------------------------------
8730 
8732 {
8733  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
8734  bool ClockState = Utilities->Clock2Stopped;
8735 
8736  Utilities->Clock2Stopped = true;
8737  // new section dealing with Snt & Snt-sh additions
8738  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
8739  // clock tick after stops flashing
8741  {
8742  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8743  {
8744  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
8745  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
8746  TActionEventType EventType = NoEvent;
8747  if(AVEntry0.Command == "Snt")
8748  {
8749  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8750  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8751  int IncrementalMinutes = 0;
8752  int IncrementalDigits = 0;
8753  if(AVEntryLast.FormatType == Repeat)
8754  {
8755  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8756  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8757  }
8758  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8759  {
8760  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
8761  }
8762  // see above note
8763 
8764  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8765  {
8766  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
8767  if(TTOD.RunningEntry != NotStarted)
8768  {
8769  continue;
8770  }
8771  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
8772  {
8773  break; // all the rest will also be greater
8774  }
8775  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
8776  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8777  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
8778  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
8779  {
8780  TTOD.TrainID = TrainVector.back().TrainID;
8781  TTOD.RunningEntry = Running;
8782  }
8783  else if(EventType == FailTrainEntry)
8784  {
8785  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8786  }
8787  }
8788  }
8789  if(AVEntry0.Command == "Snt-sh")
8790  // just start this once, shuttle repeats take care of restarts
8791  {
8792  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8793  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8794  int IncrementalMinutes = 0;
8795  int IncrementalDigits = 0;
8796  if(AVEntryLast.FormatType == Repeat)
8797  {
8798  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8799  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8800  }
8801  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8802  {
8803  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
8804  }
8805  // see above note
8806  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
8807  if(TTOD.RunningEntry == NotStarted)
8808  {
8809  if(AVEntry0.EventTime <= TTClockTime)
8810  {
8811  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8812  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
8813  TDEntry.SignallerSpeed, false, EventType))
8814  // false for SignallerControl
8815  {
8816  TTOD.TrainID = TrainVector.back().TrainID;
8817  TTOD.RunningEntry = Running;
8818  }
8819  else if(EventType == FailTrainEntry)
8820  {
8821  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8822  }
8823  }
8824  }
8825  }
8826  }
8827  }
8828  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
8829  // iteration, next cycle will catch up with any other pending updates
8830  if(!TrainVector.empty())
8831  {
8832  TrainAdded = false;
8833  AllRoutes->CallonVector.clear();
8834  // this will be rebuilt during the calls to UpdateTrain
8835  for(unsigned int x = 0; x < TrainVector.size(); x++)
8836  {
8837  TrainVectorAt(33, x).UpdateTrain(0);
8838  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
8839  // added HasTrainGone() condition in v0.4c to prevent 2 trains both having TrainGone set
8840  // at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
8841  // iterates in reverse to erase the second train to have gone, but afterwards ReplotTrains iterates forwards and therefore replots the first train to
8842  // have gone and therefore sets the TrainIDOnElement value to the exited train, with nothing to reset it. Hovering the mouse over that element with
8843  // train information enabled causes an error because the track element thinks the train is still there, whereas it is missing from the TrainVector.
8844  {
8845  break;
8846  }
8847  }
8848  // set warning flags
8849  CrashWarning = false;
8850  DerailWarning = false;
8851  SPADWarning = false;
8852  CallOnWarning = false;
8853  SignalStopWarning = false;
8854  BufferAttentionWarning = false;
8855  TrainFailedWarning = false;
8856  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
8857  {
8858  TTrain &Train = TrainVectorAt(34, x);
8859  if(Train.Crashed)
8860  // can't use background colours for crashed & derailed because same colour
8861  {
8862  CrashWarning = true;
8863  }
8864  else if(Train.Derailed)
8865  // can't use background colours for crashed & derailed because same colour
8866  {
8867  DerailWarning = true;
8868  }
8869  else if(Train.BackgroundColour == clSPADBackground)
8870  // use colour as that changes as soon as passes signal
8871  {
8872  SPADWarning = true;
8873  }
8874  else if(Train.BackgroundColour == clTrainFailedBackground)
8875  {
8876  TrainFailedWarning = true;
8877  }
8878  else if(Train.BackgroundColour == clCallOnBackground)
8879  // use colour as also stopped at signal
8880  {
8881  CallOnWarning = true;
8882  }
8883  else if(Train.BackgroundColour == clSignalStopBackground)
8884  // use colour to distinguish from call-on
8885  {
8886  SignalStopWarning = true;
8887  }
8888  else if(Train.BackgroundColour == clBufferAttentionNeeded)
8889  // use colour to distinguish from ordinary buffer stop
8890  {
8891  BufferAttentionWarning = true;
8892  }
8893  if(Train.HasTrainGone())
8894  {
8895  AnsiString Loc = "";
8896  bool ElementFound = false;
8897  TTrackElement TE;
8898  if(Train.LagElement > -1)
8899  {
8900  TE = Track->TrackElementAt(531, Train.LagElement);
8901  ElementFound = true;
8902  }
8903  else if(Train.MidElement > -1)
8904  {
8905  TE = Track->TrackElementAt(779, Train.MidElement);
8906  ElementFound = true;
8907  }
8908  else if(Train.LeadElement > -1)
8909  {
8910  TE = Track->TrackElementAt(780, Train.LeadElement);
8911  ElementFound = true;
8912  }
8913  if(ElementFound)
8914  {
8915  if(TE.ActiveTrackElementName != "")
8916  {
8917  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8918  }
8919  else
8920  {
8921  Loc = "track element " + TE.ElementID;
8922  }
8923  }
8924  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
8925  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
8926  // need above first because may also have ActionVectorEntryPtr == "Fer"
8927  {
8928  Train.UnplotTrain(9);
8929  // added at v1.3.0 to reset signals after train removed from an autosigsroute
8931  {
8934  }
8935  // end of addition
8936  AllRoutes->RebuildRailwayFlag = true;
8937  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
8938  // correctly after a crash
8939  }
8940  else if(AVEntryPtr->Command == "Fer")
8941  {
8942  bool CorrectExit = false;
8943  if(!AVEntryPtr->ExitList.empty())
8944  {
8945  for(TExitListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
8946  {
8947  if(*ELIT == Train.LagElement)
8948  {
8949  CorrectExit = true;
8950  }
8951  }
8952  }
8953  if(CorrectExit)
8954  {
8955  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
8956  }
8957  else
8958  {
8959  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
8960  }
8961  }
8962  else
8963  {
8964  if(!AVEntryPtr->SignallerControl)
8965  {
8966  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
8967  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
8968  // -2 is marker for send messages for all remaining actions except Fer if present
8969  }
8970  else
8971  {
8972  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
8973  }
8974  }
8975  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
8976  Train.DeleteTrain(1);
8977  TrainVector.erase(TrainVector.begin() + x);
8978  ReplotTrains(1, Display);
8979  // to reset ElementIDs for remaining trains when have removed a train
8980  }
8981  }
8982  }
8983  else
8984  {
8985  // reset all flags in case last train removed with flag set
8986  CrashWarning = false;
8987  DerailWarning = false;
8988  SPADWarning = false;
8989  CallOnWarning = false;
8990  SignalStopWarning = false;
8991  BufferAttentionWarning = false;
8992  TrainFailedWarning = false;
8993  }
8994  // update OpTimeToActMultimap
8996  {
8998  // clears entries then adds values for running trains then for continuation entries
8999  }
9000  Utilities->Clock2Stopped = ClockState;
9001  Utilities->CallLogPop(723);
9002 }
9003 
9004 // ---------------------------------------------------------------------------
9006 {
9007  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
9008  if(!TrainVector.empty())
9009  {
9010  for(int x = TrainVector.size() - 1; x >= 0; x--)
9011  {
9012  TrainVectorAt(50, x).DeleteTrain(2);
9013  }
9014  TrainVector.clear();
9015  }
9016  if(!TrainDataVector.empty())
9017  {
9018  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9019  {
9020  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
9021  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9022  {
9023  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
9024  TOD.RunningEntry = NotStarted;
9025  TOD.TrainID = -1;
9026  TOD.EventReported = NoEvent;
9027  }
9028  }
9029  }
9030  Display->GetOutputLog1()->Caption = "";
9031  Display->GetOutputLog2()->Caption = "";
9032  Display->GetOutputLog3()->Caption = "";
9033  Display->GetOutputLog4()->Caption = "";
9034  Display->GetOutputLog5()->Caption = "";
9035  Display->GetOutputLog6()->Caption = "";
9036  Display->GetOutputLog7()->Caption = "";
9037  Display->GetOutputLog8()->Caption = "";
9038  Display->GetOutputLog9()->Caption = "";
9039  Display->GetOutputLog10()->Caption = "";
9040  Utilities->CallLogPop(1352);
9041 }
9042 
9043 // ---------------------------------------------------------------------------
9044 
9046 {
9047  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
9048  if(!TrainVector.empty())
9049  {
9050  for(unsigned int x = 0; x < TrainVector.size(); x++)
9051  {
9052  TrainVectorAt(51, x).PlotTrain(4, Disp);
9053  }
9054  }
9055  Utilities->CallLogPop(724);
9056 }
9057 
9058 // ---------------------------------------------------------------------------
9059 
9060 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
9061 {
9062  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
9063  if(!TrainVector.empty())
9064  {
9065  for(unsigned int x = 0; x < TrainVector.size(); x++)
9066  {
9067  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
9068  }
9069  }
9070  Utilities->CallLogPop(1707);
9071 }
9072 
9073 // ---------------------------------------------------------------------------
9074 
9076 {
9077  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
9078  if(!TrainVector.empty())
9079  {
9080  for(unsigned int x = 0; x < TrainVector.size(); x++)
9081  {
9082  TrainVectorAt(52, x).UnplotTrain(10);
9083  }
9084  }
9086  Utilities->CallLogPop(725);
9087 }
9088 
9089 // ---------------------------------------------------------------------------
9090 
9091 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
9092  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
9093  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
9094 {
9095  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
9096  "," + AnsiString(Mass) + "," + ModeStr);
9097  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
9098  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr + "," + HeadCode);
9099 
9100  int RearExitPos = -1;
9101 
9102  for(int x = 0; x < 4; x++)
9103  {
9104  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
9105  {
9106  RearExitPos = x;
9107  }
9108  }
9109  if(RearExitPos == -1)
9110  {
9111  throw Exception("Error, RearExit == -1 in AddTrain");
9112  }
9113  bool ReportFlag = true;
9114 
9115  // used to stop repeated messages from CheckStartAllowable when split failed
9116  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
9117  {
9118  ReportFlag = false;
9119  }
9120  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
9121  {
9122  // messages sent to performance log in CheckStartAllowable if ReportFlag true
9123  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
9124  Utilities->CallLogPop(938);
9125  return(false);
9126  }
9127  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
9128  TTrainMode TrainMode = NoMode;
9129 
9130  if(ModeStr == "Timetable")
9131  {
9132  TrainMode = Timetable;
9133  }
9134  // all else gives 'None', 'Signaller' set within program
9135 
9136  if(MaxRunningSpeed < 10)
9137  {
9138  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
9139  }
9140  if(SignallerSpeed < 10)
9141  {
9142  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
9143  }
9144  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
9145  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
9146 
9147  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
9148  // initialise here rather than in TTrain constructor as create trains
9149  // with Null TrainDataEntryPtr when loading session trains
9150  if(SignallerControl)
9151  {
9152  NewTrain->TimetableFinished = true;
9153  NewTrain->SignallerStoppingFlag = false;
9154  NewTrain->TrainMode = Signaller;
9155  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
9156  {
9157  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
9158  }
9160  }
9161  // deal with starting conditions:-
9162  // unlocated Snt: just report entry & advance pointer
9163  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
9164  // Sns doesn't need a new train
9165  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
9166  // covers all above located starts
9167  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
9168  // wouldn't have accepted the timetable
9169  {
9170  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
9171  // StoppedAtBuffers is set in UpdateTrain()
9172  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
9173  // buffer end must be ahead of train or would have failed start position check
9174  {
9175  NewTrain->StoppedAtLocation = true;
9176  NewTrain->PlotStartPosition(0);
9178  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9179  NewTrain->ActionVectorEntryPtr->Warning);
9180  if(!SignallerControl) // don't advance if SignalControlEntry
9181  {
9182  NewTrain->ActionVectorEntryPtr++;
9183  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
9184  }
9185  NewTrain->LastActionTime = TTClockTime;
9186  }
9187  // else a through station stop
9188  else
9189  {
9190  NewTrain->StoppedAtLocation = true;
9191  NewTrain->PlotStartPosition(10);
9193  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9194  NewTrain->ActionVectorEntryPtr->Warning);
9195  if(!SignallerControl) // don't advance if SignalControlEntry
9196  {
9197  NewTrain->ActionVectorEntryPtr++;
9198  }
9199  NewTrain->LastActionTime = TTClockTime;
9200  }
9201  }
9202  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
9203  {
9204  NewTrain->PlotStartPosition(11);
9205  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
9206  AnsiString Loc = "";
9207  if(TE.ActiveTrackElementName != "")
9208  {
9209  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9210  }
9211  else
9212  {
9213  Loc = "track element " + TE.ElementID;
9214  }
9215  if(TE.TrackType == Continuation)
9216  {
9217  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9218  }
9219  else
9220  {
9221  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9222  }
9223  if(!SignallerControl) // don't advance if SignalControlEntry
9224  {
9225  NewTrain->ActionVectorEntryPtr++;
9226  }
9227  NewTrain->LastActionTime = TTClockTime;
9228  // no need to set LastActionTime for an unlocated entry
9229  }
9230  // cancel a wrong-direction route if either element of train starts on one
9231  if(NewTrain->LeadElement > -1)
9232  {
9233  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
9234  }
9235  if(NewTrain->MidElement > -1)
9236  {
9237  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
9238  }
9239  // set signals for a right-direction autosigs route for either element of train on one
9240  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
9241  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
9242  int RouteNumber = -1;
9243  bool SignalsSet = false;
9244 
9245  if(NewTrain->LeadElement > -1)
9246  {
9247  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9248  {
9249  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9250  int RouteStartPosition;
9251  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9252  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
9253  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
9254  if(FirstPair.first == RouteNumber)
9255  {
9256  RouteStartPosition = FirstPair.second;
9257  }
9258  else if(SecondPair.first == RouteNumber)
9259  {
9260  RouteStartPosition = SecondPair.second;
9261  }
9262  else
9263  {
9264  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
9265  }
9266  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
9267  SignalsSet = true;
9268  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9269  }
9270  else if(RouteNumber > -1) // non-autosigsroute
9271  {
9272  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
9273  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9274  int FirstELinkPos = TempPDE.GetELinkPos();
9275  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
9276  {
9277  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9278  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
9279  }
9280  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
9281  {
9282  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9283  // remove the last element under LeadElement
9284  }
9285  AllRoutes->RebuildRailwayFlag = true;
9286  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9287  // now deal with a rear linked autosigs route
9288  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
9289  {
9290  int LinkedRouteNumber = -1;
9291  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
9292  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9293  {
9294  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
9295  // this is ok as here we are setting signals from the start of the route
9296  }
9297  }
9298  SignalsSet = true;
9299  }
9300  }
9301  if(NewTrain->MidElement > -1)
9302  // if entering at a continuation MidElement == -1
9303  {
9304  // this is included in case a train starts with LeadElement on no route and MidElement on a route
9305  if(!SignalsSet)
9306  {
9307  RouteNumber = -1;
9308  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9309  {
9310  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9311  int RouteStartPosition;
9312  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9313  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
9314  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
9315  if(FirstPair.first == RouteNumber)
9316  {
9317  RouteStartPosition = FirstPair.second;
9318  }
9319  else if(SecondPair.first == RouteNumber)
9320  {
9321  RouteStartPosition = SecondPair.second;
9322  }
9323  else
9324  {
9325  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
9326  }
9327  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
9328  SignalsSet = true;
9329  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9330  }
9331  else if(RouteNumber > -1) // non-autosigsroute
9332  {
9333  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
9334  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9335  int FirstELinkPos = TempPDE.GetELinkPos();
9336  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
9337  {
9338  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9339  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
9340  }
9341  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
9342  {
9343  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9344  // remove the last element under LeadElement
9345  }
9346  AllRoutes->RebuildRailwayFlag = true;
9347  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9348  // now deal with a rear linked autosigs route
9349  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
9350  {
9351  int LinkedRouteNumber = -1;
9352  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
9353  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9354  {
9355  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
9356  // this is ok as now we are setting signals from the start of the route
9357  }
9358  }
9359  }
9360  }
9361  }
9362  TrainVector.push_back(*NewTrain);
9363  Utilities->CallLogPop(731);
9364  return(true);
9365 }
9366 
9367 // ---------------------------------------------------------------------------
9368 
9369 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
9370 {
9371  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
9372  AnsiString(TrackVectorNumber));
9373  int VecPos = -1;
9374 
9375  for(unsigned int x = 0; x < TrainVector.size(); x++)
9376  {
9377  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
9378  {
9379  VecPos = x;
9380  }
9381  }
9382  if(VecPos == -1)
9383  {
9384  throw Exception("Error, VecPos not set in EntryPos");
9385  }
9386  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
9387  {
9388  Utilities->CallLogPop(734);
9389  return(TrainVectorAt(3, VecPos).LeadEntryPos);
9390  }
9391  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
9392  {
9393  Utilities->CallLogPop(735);
9394  return(TrainVectorAt(5, VecPos).MidEntryPos);
9395  }
9396  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
9397  {
9398  Utilities->CallLogPop(736);
9399  return(TrainVectorAt(7, VecPos).LagEntryPos);
9400  }
9401  Utilities->CallLogPop(737);
9402  return(-1);
9403 }
9404 
9405 // ---------------------------------------------------------------------------
9406 
9408 {
9409  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
9410  for(unsigned int x = 0; x < TrainVector.size(); x++)
9411  {
9412  if(TrainVectorAt(53, x).TrainID == TrainID)
9413  {
9414  Utilities->CallLogPop(738);
9415  return(TrainVectorAt(54, x));
9416  }
9417  }
9418  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
9419 }
9420 
9421 // ---------------------------------------------------------------------------
9422 
9423 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
9424 // return true if find the train (added at v2.4.0 as can select a removed train
9425 // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
9426 {
9427  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
9428  for(unsigned int x = 0; x < TrainVector.size(); x++)
9429  {
9430  if(TrainVectorAt(69, x).TrainID == TrainID)
9431  {
9432  Utilities->CallLogPop(2152);
9433  return(true);
9434  }
9435  }
9436  Utilities->CallLogPop(2153);
9437  return(false);
9438 }
9439 
9440 // ---------------------------------------------------------------------------
9441 
9442 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
9443 {
9444  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
9445  Utilities->Format96HHMMSS(Time));
9446  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
9447 
9448  Utilities->CallLogPop(2061);
9449  return(RepeatTime);
9450 }
9451 
9452 // ---------------------------------------------------------------------------
9453 
9454 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
9455 // Enter with Ptr pointing to first action to be listed (i.e. next action)
9456 {
9457  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
9458  AnsiString RetStr = "", PartStr = "";
9459  int Count = 0;
9460  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
9461 
9462  Ptr--; // because incremented at start of loop
9463  do
9464  {
9465  Ptr++;
9466  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
9467  {
9468  continue; // move past the starting entry
9469  }
9470  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
9471  {
9472  break;
9473  }
9474  if(Ptr->SignallerControl)
9475  {
9476  RetStr = "Train under signaller control";
9477  break;
9478  }
9479  if(Ptr->FormatType == TimeTimeLoc)
9480  {
9481  if(Ptr->ArrivalTime == Ptr->DepartureTime)
9482  {
9483  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
9484  }
9485  else
9486  {
9487  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
9488  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9489  Count++; // because there are 2 entries
9490  }
9491  }
9492  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
9493  {
9494  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
9495  }
9496  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
9497  {
9498  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9499  }
9500  else if(Ptr->FormatType == PassTime) // new
9501  {
9502  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
9503  }
9504  else if(Ptr->Command == "Fns")
9505  {
9506  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9507  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9508  PartStr = ControllerCheckNewServiceDepartureTime(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
9509  }
9510  else if(Ptr->Command == "F-nshs")
9511  {
9512  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9513  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
9514  PartStr = ControllerCheckNewServiceDepartureTime(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9515  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
9516  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
9517  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
9518  }
9519 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
9520  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9521  {
9522  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9523  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9524  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9525  PartStr = ControllerCheckNewServiceDepartureTime(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9526  }
9527  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9528  {
9529  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9530  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
9531  PartStr = ControllerCheckNewServiceDepartureTime(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9532  }
9533  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9534  {
9535  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9536  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9537  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9538  PartStr = ControllerCheckNewServiceDepartureTime(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9539  }
9540  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9541  {
9542  PartStr = "Terminate at " + Ptr->LocationName;
9543  }
9544  else if(Ptr->Command == "Frh")
9545  {
9546  PartStr = "Terminate at " + Ptr->LocationName;
9547  }
9548  else if(Ptr->Command == "Fer")
9549  {
9550  AnsiString AllowedExits;
9551  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
9552  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
9553  }
9554  else if(Ptr->Command == "Fjo")
9555  {
9556  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
9557  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9558  }
9559  else if(Ptr->Command == "jbo")
9560  {
9561  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
9562  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9563  }
9564  else if(Ptr->Command == "fsp")
9565  {
9566  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
9567  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9568  }
9569  else if(Ptr->Command == "rsp")
9570  {
9571  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
9572  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9573  }
9574  else if(Ptr->Command == "cdt")
9575  {
9576  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
9577  }
9578  if(RetStr != "")
9579  {
9580  RetStr = RetStr + '\n' + PartStr;
9581  }
9582  else
9583  {
9584  RetStr = PartStr;
9585  }
9586  Count++;
9587  }
9588  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
9589  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
9590  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
9591  // forward as anyone should wish to see without looking at the full timetable
9592  Utilities->CallLogPop(2072);
9593  return(RetStr);
9594 }
9595 
9596 // ---------------------------------------------------------------------------
9597 
9598 AnsiString TTrainController::ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
9599 {
9600  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
9601  + AnsiString(RptNum) + ",ControllerCheckNewServiceDepartureTime," + TDEPtr->HeadCode);
9602  AnsiString DepTime = "", EventTime = "";
9603  bool CDTFlag = false; //reports if train changes direction before departs
9604  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
9605  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
9606  {
9607  if(AVI->Command == "cdt")
9608  {
9609  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
9610  continue;
9611  }
9612  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
9613  {
9614  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
9615  RetStr += "\nNew service splits at " + EventTime;
9616  Utilities->CallLogPop(2237);
9617  return(RetStr);
9618  }
9619  if(AVI->Command == "jbo")
9620  {
9621  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
9622  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
9623  Utilities->CallLogPop(2238);
9624  return(RetStr);
9625  }
9626  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
9627  {
9628  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
9629  if(CDTFlag)
9630  {
9631  RetStr += "\nNew service changes direction then departs at " + DepTime;
9632  }
9633  else
9634  {
9635  RetStr += "\nNew service departs at " + DepTime;
9636  }
9637  Utilities->CallLogPop(2239);
9638  return(RetStr);
9639  }
9640  }
9641  Utilities->CallLogPop(2223);
9642  return(RetStr);
9643 }
9644 
9645 // ---------------------------------------------------------------------------
9646 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
9647 /*
9648  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
9649  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
9650  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
9651 
9652  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
9653  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
9654  user wishes
9655 
9656  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9657  descriptive text or anything user wishes
9658  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9659  be ignored) is taken as the timetable start time.
9660  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9661  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9662  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9663  within the timetable if required.
9664  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9665  services)
9666  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9667  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9668 
9669  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9670  text timetable file easier
9671 
9672  form:-
9673  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9674  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9675  then multiple entries, separated by commas, of the form:-
9676 
9677  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9678  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
9679  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
9680 
9681  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
9682  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
9683  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
9684 
9685  HH:MM;Command (cdt) }TimeCmd }
9686  HH:MM;Location (arr & dep) }TimeLoc }
9687  HH:MM;HH:MM;Location }TimeTimeLoc }
9688  HH:MM;pas;Location }PassTime }
9689  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
9690  HH:MM;Fer;set of allowable IDs }ExitRailway }
9691  Command (Frh only) }FinRemHere }
9692 
9693  R;mm;dd;nn. Repeat Repeat entry
9694 
9695  Formats:
9696 
9697  Command only: Frh
9698  Time;Command: cdt
9699  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
9700  Time;Command;2 Element IDs: Snt
9701  Time;Comand;n Element IDs: Fer
9702  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
9703  Time;Command;2 Element IDs;Headcode Snt-sh
9704  Time;Command;Location pas
9705  Time;Location Arr Dep
9706  Time;Time;Location Arr & dep together
9707 
9708  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
9709 
9710  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
9711  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
9712  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
9713 
9714  4 2x Linked entries, all shuttles:
9715 
9716  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
9717  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
9718  -> Remain Here (at finish location after all repeats)
9719  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
9720  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
9721 
9722  Allowable successors:-
9723 
9724  Successor state Type
9725 
9726  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
9727  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
9728  Sfs AtLoc )
9729  Sns AtLoc ) Start
9730  Sns-fsh AtLoc )
9731  Snt-sh AtLoc )
9732  Sns-sh AtLoc )
9733 
9734  pas Moving )
9735  jbo AtLoc )
9736  fsp AtLoc )
9737  rsp AtLoc ) Intermediate
9738  cdt AtLoc )
9739  TimeLoc arr Moving (bef) )
9740  TimeLoc dep AtLoc (bef) )
9741  TimeTimeLoc Moving )
9742 
9743  Fns Repeat/Nothing)
9744  Fjo Repeat/Nothing)
9745  Frh Repeat/Nothing)
9746  Fer Repeat/Nothing) Finish
9747  Frh-sh Repeat )
9748  Fns-sh Repeat )
9749  F-nshs Nothing )
9750 
9751  Descriptions:
9752  Snt New train
9753  Sfs New service from split
9754  Sns New service from another service
9755  Sns-fsh New non-repeating service from a shuttle service
9756  Snt-sh New shuttle train at a timetabled stop
9757  Sns-sh New shuttle service from a feeder service
9758 
9759  pas Pass
9760  jbo Be joined by another train
9761  fsp Front split
9762  rsp Rear split
9763  cdt Change direction of train
9764  TimeLoc arr Arrival
9765  TimeLoc dep Departure
9766  TimeTimeLoc Arrival and departure
9767 
9768  Fns Finish & form a new service
9769  Fjo Finish & join another train
9770  Frh Finish & remain here
9771  Fer Finish & exit railway
9772  Frh-sh Finish & repeat shuttle, finally remain here
9773  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
9774  F-nshs Finish & form a shuttle feeder service
9775 */
9776 
9777 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
9778 {
9779  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
9780  // a line that is too long; timetable containing too few lines; and timetable failed to open.
9781  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
9782  // new for v0.2b
9783  // compile ActiveTrackElementNameMap
9784  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
9785 
9787  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
9788  {
9789  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
9791  == Track->ContinuationNameMap.end())
9792  {
9793  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
9794  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
9795  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
9796  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
9797  }
9798  }
9800  // end of new section
9801  std::ifstream TTBLFile(FileName, std::ios_base::binary);
9802 
9803  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
9804  if(TTBLFile.is_open())
9805  {
9806  char *TrainTimetableString = new char[10000];
9807  // enough for over 200 stations, should be adequate!
9808  bool EndOfFile = false;
9809  int Count = 0;
9810  // counts 'relevant' lines, i.e ignores any before the start time on its own line
9811  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9812  // delimiter is '\0' as it's an AnsiString
9813  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9814  // file empty - stores a null in 1st position if doesn't load any characters
9815  {
9816  // may still have eof even if read a line (no CRLF at end), and
9817  // if so need to process it
9818  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
9819  TTBLFile.close();
9820  delete[] TrainTimetableString;
9821  Utilities->CallLogPop(1611);
9822  return(false);
9823  }
9824  AnsiString OneLine(TrainTimetableString);
9825  bool FinalCallFalse = false;
9826  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9827  // get rid of lines before the start time
9828  {
9829  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
9830  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9831  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9832  // stores a null in 1st position if doesn't load any characters
9833  {
9834  // may still have eof even if read a line (no CRLF at end), and
9835  // if so need to process it
9836  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
9837  TTBLFile.close();
9838  delete[] TrainTimetableString;
9839  Utilities->CallLogPop(772);
9840  return(false);
9841  }
9842  OneLine = AnsiString(TrainTimetableString);
9843  }
9844  // here when have accepted the start time
9845  Count++; // increment past the start time
9846  while(!EndOfFile)
9847  {
9848  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9849  // get next line after start time
9850  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9851  // stores a null in 1st position if doesn't load any characters
9852  {
9853  // may still have eof even if read a line (no CRLF at end), and
9854  // if so need to process it
9855  EndOfFile = true;
9856  OneLine = "";
9857  }
9858  else
9859  {
9860  OneLine = AnsiString(TrainTimetableString);
9861  }
9862  if(OneLine.Length() > 9999)
9863  {
9864  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
9865  TTBLFile.close();
9866  delete[] TrainTimetableString;
9867  Utilities->CallLogPop(789);
9868  return(false);
9869  }
9870  bool FinalCallFalse = false;
9871  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9872  // false for FinalCall - just checking at this stage
9873  {
9874  TTBLFile.close();
9875  delete[] TrainTimetableString;
9876  Utilities->CallLogPop(770);
9877  return(false);
9878  }
9879  if(EndOfFile && (Count < 2))
9880  // Timetable must contain at least two relevant lines, one for start time and at least one train
9881  {
9882  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
9883  TTBLFile.close();
9884  delete[] TrainTimetableString;
9885  Utilities->CallLogPop(771);
9886  return(false);
9887  }
9888  Count++;
9889  }
9890  delete[] TrainTimetableString;
9891  TTBLFile.close();
9892  } // if(TTBLFile.is_open())
9893  else
9894  {
9895  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
9896  Utilities->CallLogPop(2154);
9897  return(false);
9898  }
9899  Utilities->CallLogPop(753);
9900  return(true);
9901 }
9902 
9903 // ---------------------------------------------------------------------------
9904 
9905 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
9906  bool CheckLocationsExistInRailway) // return true for success
9907 
9908 /* Format:
9909  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9910  descriptive text or anything user wishes
9911  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9912  be ignored) is taken as the timetable start time.
9913  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9914  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9915  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9916  within the timetable if required.
9917  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9918  services)
9919  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9920  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9921 
9922  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9923  text timetable file easier
9924 
9925  form:-
9926  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9927  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9928  then multiple entries, separated by commas, of the form:-
9929 
9930  Format FormatType
9931  [W]HH:MM;Command (cdt) }TimeCmd }
9932  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
9933  [W]HH:MM;pas;Location }PassTime }
9934  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9935  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
9936  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
9937  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
9938  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
9939  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
9940  [W]HH:MM;Fns-sh;Details }FSHNewService }
9941  [W]HH:MM;Location (arr & dep) }TimeLoc }
9942  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
9943  Command (Frh only) }FinRemHere }
9944 
9945  R;mm;dd;nn. Repeat Repeat entry
9946 
9947  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
9948  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
9949  at location; or (c) departure time if train already at location (including train started at location either as a new
9950  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
9951  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
9952  minutes, incremental train headcode last 2 digits, and number of repeats.
9953 
9954  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
9955  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
9956  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
9957  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
9958  (it's for a shuttle train to return to depot at end of services)
9959 
9960  Command/Location & details are as follows:-
9961 
9962  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
9963  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
9964  2E44 in its Sfs entry. All these are checked.
9965  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
9966 
9967  Start commands:-
9968  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
9969  with loc as a start entry can't have a location as details)
9970  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
9971  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
9972  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
9973  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
9974  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
9975  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
9976  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
9977  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
9978  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
9979  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
9980  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
9981 
9982  Intermediate commands:-
9983  Time - Location (TimeLoc), can be arrival or departure depending on context
9984  Time Time location (TimeTimeLoc), arrival and departure
9985  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
9986  pas (PassTime), Time;pas;Location
9987  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
9988  joining train's finish details must correspond or the file check will fail
9989  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
9990  new train - that train's starting information must correspond)
9991  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
9992  new train - that train's starting information must correspond)
9993  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
9994 
9995  Finish commands:-
9996  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
9997  creation)
9998  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
9999  shuttle headcode (no train creation)
10000  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
10001  may have to wait for it), details = new headcode (delete train)
10002  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
10003  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
10004  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
10005  here
10006  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
10007  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
10008 
10009  Repeat:-
10010  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
10011  headcodes - it is up to user to avoid duplicates if he/she wishes to.
10012 
10013  Checks carried out with error messages in this function:-
10014  At least one comma in a service line (it's based on a .csv file)
10015  No entries following train information;
10016  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10017  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10018  First entry not a start entry;
10019  Train information incomplete before a start entry;
10020  Entry follows a finish entry but doesn't begin with 'R';
10021  SplitEntry returns false in a finish entry - message repeats the entry for information;
10022  Last action entry isn't a finish entry.
10023 
10024  Function returns false with no message if:-
10025  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10026  time is found at all then an error message is given in the calling function);
10027  SplitTrainInfo returns false (message given in called function);
10028  SplitRepeat returns false (message given in called function).
10029 */{
10030  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
10031  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
10032  TTrainDataEntry TempTrainDataEntry;
10033 
10034  EndOfFile = false;
10035  StripSpaces(0, OneLine);
10036  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
10037  // semicolons within the line
10038  ServiceReference = "";
10039  if(OneLine != "")
10040  {
10041  if(OneLine[1] != '*')
10042  {
10043  int SCPos = OneLine.Pos(';');
10044  if(SCPos == 0)
10045  {
10046  ServiceReference = OneLine.SubString(1, 8);
10047  }
10048  else
10049  {
10050  ServiceReference = OneLine.SubString(1, (SCPos - 1));
10051  }
10052  }
10053  }
10054  bool AllCommas = true;
10055 
10056  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
10057  {
10058  if(OneLine[x] != ',')
10059  {
10060  AllCommas = false;
10061  }
10062  }
10063  if(AllCommas || (OneLine == ""))
10064  {
10065  if(Count > 0)
10066  {
10067  EndOfFile = true;
10068  // returns true for a blank line - treated as end of file
10069  Utilities->CallLogPop(1018);
10070  return(true);
10071  }
10072  else // count == 0 so not yet found a start time, no message to be given
10073  {
10074  Utilities->CallLogPop(754);
10075  return(false);
10076  }
10077  }
10078  AnsiString First = "", Second = "", Third = "", Fourth = "";
10079  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
10080  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
10081  TDateTime StartTime(0);
10082  TExitList ExitList;
10083  bool Warning = false;
10084 
10085  if(Count == 0) // no start time found yet
10086  {
10087 /* dropped at v0.6b
10088  AnyHeadCodeValid = false;
10089  if(OneLine.SubString(6,5) == ";0000")
10090  {
10091  AnyHeadCodeValid = true;
10092  }
10093 */
10094  if(!CheckTimeValidity(0, OneLine, StartTime))
10095  {
10096  // no message is given for an invalid time as it's assumed to be an irrelevant line
10097  // if no start time is found at all then an error message is given in the calling function
10098  // AnyHeadCodeValid = false;
10099  Utilities->CallLogPop(755);
10100  return(false);
10101  }
10102  if(FinalCall) // here if start time valid
10103  {
10104  TTClockTime = StartTime;
10105  TimetableStartTime = StartTime;
10106  }
10107  }
10108  else
10109  {
10110  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
10111  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
10112  double MaxBrakeRate = 0;
10113  double PowerAtRail = 0;
10114  int SignallerSpeed = 0;
10115  if(OneLine[1] == '*')
10116  {
10117  Utilities->CallLogPop(1581);
10118  return(true);
10119  // ignore any line beginning with '*' but return true as there is no error
10120  }
10121  int Pos = OneLine.Pos(',');
10122  if(Pos == 0)
10123  {
10124  int SubStringLength = 20;
10125  if(OneLine.Length() < 20)
10126  {
10127  SubStringLength = OneLine.Length();
10128  }
10129  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
10130  Utilities->CallLogPop(766);
10131  return(false);
10132  }
10133  TrainInfoStr = OneLine.SubString(1, Pos - 1);
10134  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
10135  GiveMessages)) // error messages given in SplitTrainInfo
10136  {
10137  Utilities->CallLogPop(773);
10138  return(false);
10139  }
10140  if(FinalCall)
10141  {
10142  // store Train info - conversions done in SplitTrainInfo
10143  // only headcode mandatory for continuing services
10144  TempTrainDataEntry.HeadCode = HeadCode;
10145  TempTrainDataEntry.ServiceReference = HeadCode;
10146  TempTrainDataEntry.Description = Description;
10147  TempTrainDataEntry.StartSpeed = StartSpeed;
10148  TempTrainDataEntry.Mass = Mass;
10149  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
10150  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
10151  TempTrainDataEntry.PowerAtRail = PowerAtRail;
10152  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
10153  TTrainOperatingData TempTrainOperatingData;
10154  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
10155  }
10156  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
10157  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
10158  // so strip them off
10159  while(NewRemainder[NewRemainder.Length()] == ',')
10160  {
10161  if(NewRemainder.Length() > 1)
10162  {
10163  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
10164  }
10165  else
10166  {
10167  NewRemainder = "";
10168  break;
10169  }
10170  }
10171  // check if zero length & fail if so
10172  if(NewRemainder == "")
10173  {
10174  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
10175  Utilities->CallLogPop(769);
10176  return(false);
10177  }
10178  // now have one more entry than there are commas
10179  int CommaCount = 0;
10180  for(int x = 1; x < NewRemainder.Length() + 1; x++)
10181  {
10182  if(NewRemainder[x] == ',')
10183  {
10184  CommaCount++;
10185  }
10186  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
10187  if(CommaCount == 0)
10188  {
10189  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
10190  {
10191  int SubStringLength = 20;
10192  if(OneLine.Length() < 20)
10193  {
10194  SubStringLength = OneLine.Length();
10195  }
10196  TimetableMessage(GiveMessages,
10197  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
10198  OneLine.SubString(1, SubStringLength) + "'....");
10199  Utilities->CallLogPop(783);
10200  return(false);
10201  }
10202  }
10203  AnsiString OneEntry = "";
10204  TTimetableFormatType FormatType;
10205  TTimetableSequenceType SequenceType;
10206  TTimetableLocationType LocationType;
10207  TTimetableShuttleLinkType ShuttleLinkType;
10208  bool FinishFlag = false;
10209  for(int x = 0; x < CommaCount + 1; x++)
10210  {
10211  if((CommaCount == 0) || (x < CommaCount))
10212  // i.e. train entered under signaller control with no repeats, or entry is not the last,
10213  // in which case there's a comma & finish element or repeat still to come this entry could
10214  // be a finish but can't be a repeat
10215  {
10216  if(CommaCount == 0)
10217  {
10218  OneEntry = NewRemainder;
10219  NewRemainder = "";
10220  }
10221  else
10222  {
10223  Pos = NewRemainder.Pos(',');
10224  OneEntry = NewRemainder.SubString(1, Pos - 1);
10225  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
10226  }
10227  First = "";
10228  Second = "";
10229  Third = "";
10230  Fourth = "";
10231  RearStartOrRepeatMins = 0;
10232  FrontStartOrRepeatDigits = 0;
10233  NumberOfRepeats = 0;
10234  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10235  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10236  {
10237  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10238  Utilities->CallLogPop(756);
10239  return(false);
10240  }
10241  // check if warning for Frh or Fjo & reject
10242  if(Warning && (Second == "Frh"))
10243  {
10244  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
10245  Utilities->CallLogPop(1793);
10246  return(false);
10247  }
10248  if(Warning && (Second == "Fjo"))
10249  {
10250  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
10251  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
10252  Utilities->CallLogPop(1794);
10253  return(false);
10254  }
10255  if(x == 0) // should be start event
10256  {
10257  if(SequenceType != Start)
10258  {
10259  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
10260  Utilities->CallLogPop(784);
10261  return(false);
10262  }
10263  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
10264  {
10265  if(NewRemainder[1] != 'R')
10266  {
10267  TimetableMessage(GiveMessages,
10268  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
10269  OneEntry + "'");
10270  Utilities->CallLogPop(787);
10271  return(false);
10272  }
10273  }
10274  if((Second == "Snt") || (Second == "Snt-sh"))
10275  // need full train information including non-default values for at least HeadCode, Description,
10276  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
10277  {
10278  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
10279  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
10280  {
10281  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
10282  OneEntry + "'");
10283  Utilities->CallLogPop(1783);
10284  return(false);
10285  }
10286  }
10287  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
10288  // service continuation - need at least non-default value for HeadCode
10289  {
10290  if(HeadCode == "")
10291  {
10292  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10293  OneEntry + "'");
10294  Utilities->CallLogPop(788);
10295  return(false);
10296  }
10297  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
10298  {
10299  TimetableMessage(GiveMessages,
10300  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10301  OneEntry + "'");
10302  Utilities->CallLogPop(843);
10303  return(false);
10304  }
10305  }
10306  }
10307  if(SequenceType == Finish)
10308  {
10309  FinishFlag = true;
10310  // marker for only permitted additional entry being a repeat, only needed if the
10311  // finish entry is not the last entry
10312  }
10313  if(FinalCall)
10314  {
10315  // interpret & add to ActionVector
10316  TDateTime TempTime;
10317  TActionVectorEntry ActionVectorEntry;
10318  ActionVectorEntry.FormatType = FormatType;
10319  ActionVectorEntry.LocationType = LocationType;
10320  ActionVectorEntry.SequenceType = SequenceType;
10321  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10322  ActionVectorEntry.Warning = Warning;
10323  if(FormatType == TimeLoc)
10324  {
10325  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
10326  {
10327  ;
10328  } // these will all be true as final call
10329 
10330  ActionVectorEntry.LocationName = Second;
10331  }
10332  else if(FormatType == PassTime) // new
10333  {
10334  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
10335  {
10336  ;
10337  }
10338  ActionVectorEntry.Command = Second;
10339  ActionVectorEntry.LocationName = Third;
10340  }
10341  else if(FormatType == TimeTimeLoc)
10342  {
10343  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
10344  {
10345  ;
10346  }
10347  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
10348  {
10349  ;
10350  }
10351  ActionVectorEntry.LocationName = Third;
10352  }
10353  else if(FormatType == TimeCmd)
10354  {
10355  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
10356  {
10357  ;
10358  }
10359  ActionVectorEntry.Command = Second;
10360  }
10361  else if(FormatType == ExitRailway)
10362  {
10363  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
10364  {
10365  ;
10366  }
10367  ActionVectorEntry.Command = Second;
10368  ActionVectorEntry.ExitList = ExitList;
10369  }
10370  else if(FormatType == StartNew)
10371  {
10372  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
10373  {
10374  ;
10375  }
10376  ActionVectorEntry.Command = Second;
10377  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10378  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10379  if(Fourth == 'S')
10380  {
10381  ActionVectorEntry.SignallerControl = true;
10382  }
10383  }
10384  else if(FormatType == SNTShuttle)
10385  {
10386  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
10387  {
10388  ;
10389  }
10390  ActionVectorEntry.Command = Second;
10391  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10392  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10393  ActionVectorEntry.OtherHeadCode = Fourth;
10394  }
10395  else if(FormatType == SNSShuttle)
10396  {
10397  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
10398  {
10399  ;
10400  }
10401  ActionVectorEntry.Command = Second;
10402  ActionVectorEntry.OtherHeadCode = Third;
10403  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10404  }
10405  else if(FormatType == TimeCmdHeadCode)
10406  {
10407  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
10408  {
10409  ;
10410  }
10411  ActionVectorEntry.Command = Second;
10412  ActionVectorEntry.OtherHeadCode = Third;
10413  }
10414  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
10415  {
10416  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
10417  {
10418  ;
10419  }
10420  ActionVectorEntry.Command = Second;
10421  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10422  }
10423  else if(FormatType == FSHNewService)
10424  {
10425  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
10426  {
10427  ;
10428  }
10429  ActionVectorEntry.Command = Second;
10430  ActionVectorEntry.OtherHeadCode = Third;
10431  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10432  }
10433  else if(FormatType == FinRemHere)
10434  {
10435  ActionVectorEntry.Command = Second;
10436  }
10437  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10438  }
10439  }
10440  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
10441  {
10442  OneEntry = NewRemainder;
10443  First = "";
10444  Second = "";
10445  Third = "";
10446  Fourth = "";
10447  RearStartOrRepeatMins = 0;
10448  FrontStartOrRepeatDigits = 0;
10449  NumberOfRepeats = 0;
10450  if((FinishFlag) && (OneEntry[1] != 'R'))
10451  // already had a finish entry
10452  {
10453  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
10454  Utilities->CallLogPop(79);
10455  return(false);
10456  }
10457  if(OneEntry[1] != 'R') // must be finish
10458  {
10459  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10460  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10461  {
10462  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10463  Utilities->CallLogPop(757);
10464  return(false);
10465  }
10466  if(SequenceType != Finish)
10467  {
10468  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
10469  Utilities->CallLogPop(785);
10470  return(false);
10471  }
10472  if(FinalCall)
10473  {
10474  // interpret & add to ActionVector
10475  TDateTime TempTime;
10476  TActionVectorEntry ActionVectorEntry;
10477  ActionVectorEntry.FormatType = FormatType;
10478  ActionVectorEntry.LocationType = LocationType;
10479  ActionVectorEntry.SequenceType = SequenceType;
10480  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10481  ActionVectorEntry.Warning = Warning;
10482  if(FormatType == TimeCmd)
10483  {
10484  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
10485  {
10486  ;
10487  }
10488  ActionVectorEntry.Command = Second;
10489  }
10490  else if(FormatType == TimeCmdHeadCode)
10491  {
10492  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
10493  {
10494  ;
10495  }
10496  ActionVectorEntry.Command = Second;
10497  ActionVectorEntry.OtherHeadCode = Third;
10498  }
10499  else if(FormatType == FNSNonRepeatToShuttle)
10500  {
10501  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
10502  {
10503  ;
10504  }
10505  ActionVectorEntry.Command = Second;
10506  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10507  }
10508  else if(FormatType == FSHNewService)
10509  {
10510  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
10511  {
10512  ;
10513  }
10514  ActionVectorEntry.Command = Second;
10515  ActionVectorEntry.OtherHeadCode = Third;
10516  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10517  }
10518  else if(FormatType == ExitRailway)
10519  {
10520  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
10521  {
10522  ;
10523  }
10524  ActionVectorEntry.Command = Second;
10525  ActionVectorEntry.ExitList = ExitList;
10526  }
10527  else if(FormatType == FinRemHere)
10528  {
10529  ActionVectorEntry.Command = Second;
10530  }
10531  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10532  }
10533  }
10534  else // repeat
10535  {
10536  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
10537  {
10538  Utilities->CallLogPop(786);
10539  // error messages given in SplitRepeat
10540  return(false);
10541  }
10542  if(FinalCall)
10543  {
10544  TActionVectorEntry ActionVectorEntry;
10545  ActionVectorEntry.FormatType = Repeat;
10546  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
10547  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
10548  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
10549  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10550  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10551  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
10552  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10553  }
10554  }
10555  }
10556  }
10557  if(FinalCall)
10558  {
10559  TrainDataVector.push_back(TempTrainDataEntry);
10560  }
10561  }
10562  Utilities->CallLogPop(80);
10563  return(true);
10564 }
10565 
10566 // ---------------------------------------------------------------------------
10567 
10568 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
10569 {
10570  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
10571  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
10572  {
10573  Utilities->CallLogPop(1890);
10574  return(false);
10575  }
10576  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
10577  {
10578  Utilities->CallLogPop(1891);
10579  return(false);
10580  }
10581  Utilities->CallLogPop(1892);
10582  return(true);
10583 }
10584 
10585 // ---------------------------------------------------------------------------
10586 
10587 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
10588 // 1st 5 chars must be HH:MM, anything else will be ignored
10589 {
10590  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
10591  if(TimeStr.Length() < 5)
10592  {
10593  Utilities->CallLogPop(926);
10594  return(false);
10595  }
10596  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
10597  {
10598  Utilities->CallLogPop(927);
10599  return(false);
10600  }
10601  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
10602  {
10603  Utilities->CallLogPop(928);
10604  return(false);
10605  }
10606  if(TimeStr[3] != ':')
10607  {
10608  Utilities->CallLogPop(929);
10609  return(false);
10610  }
10611  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
10612  {
10613  Utilities->CallLogPop(930);
10614  return(false);
10615  }
10616  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
10617  {
10618  Utilities->CallLogPop(931);
10619  return(false);
10620  }
10621  while(TimeStr.Length() > 5)
10622  {
10623  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
10624  }
10625  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
10626  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
10627 
10628  if((WholeHours + FracHour) >= 95.98334)
10629  {
10630  Utilities->CallLogPop(1817);
10631  return(false); // > 95h 59m
10632  }
10633  Time = TDateTime((WholeHours + FracHour) / 24);
10634  Utilities->CallLogPop(932);
10635  return(true);
10636 }
10637 
10638 // ---------------------------------------------------------------------------
10639 
10640 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
10641  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
10642  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
10643 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
10644  Return false for failure.
10645  See description above under ProcessOneTimetableLinefor details of train action entries
10646  NB all types set except LocationType for Sns as may be located or not
10647 */{
10648  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
10649  Warning = false;
10650  TDateTime TempTime;
10651 
10652  if(OneEntry.Length() > 0)
10653  {
10654  if(OneEntry[1] == 'W') // warning
10655  {
10656  Warning = true;
10657  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
10658  // strip it off
10659  }
10660  }
10661  if(OneEntry == "Frh")
10662  {
10663  FormatType = FinRemHere;
10664  SequenceType = Finish;
10665  LocationType = AtLocation;
10666  ShuttleLinkType = NotAShuttleLink;
10667  Second = "Frh";
10668  Utilities->CallLogPop(1016);
10669  return(true);
10670  }
10671  if(OneEntry.Length() < 7)
10672  {
10673  Utilities->CallLogPop(907);
10674  return(false); // 'HH:MM;' + at least a one-letter location name
10675  }
10676  int Pos = OneEntry.Pos(';'); // first segment delimiter
10677 
10678  if(Pos != 6)
10679  {
10680  Utilities->CallLogPop(908);
10681  return(false);
10682  // no delimiter or delimiter not in position 6, has to be a time so fail
10683  }
10684  First = OneEntry.SubString(1, 5); // has to be a time
10685  if(!CheckTimeValidity(16, First, TempTime))
10686  {
10687  Utilities->CallLogPop(909);
10688  return(false);
10689  }
10690  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
10691 
10692  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
10693  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
10694  {
10695  if(Remainder.Length() < 7)
10696  {
10697  Utilities->CallLogPop(910);
10698  return(false); // 'HH:MM;' + at least a one-letter location name
10699  }
10700  Pos = Remainder.Pos(';'); // second segment delimiter
10701  if(Pos == 0)
10702  {
10703  Utilities->CallLogPop(911);
10704  return(false);
10705  // no delimiter, has to be one between departure time & location
10706  }
10707  Second = Remainder.SubString(1, 5); // has to be a time
10708  if(!CheckTimeValidity(15, Second, TempTime))
10709  {
10710  Utilities->CallLogPop(912);
10711  return(false);
10712  }
10713  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10714  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
10715  {
10716  Utilities->CallLogPop(913);
10717  return(false);
10718  }
10719  FormatType = TimeTimeLoc;
10720  SequenceType = Intermediate;
10721  LocationType = AtLocation;
10722  ShuttleLinkType = NotAShuttleLink;
10723  Utilities->CallLogPop(914);
10724  return(true);
10725  }
10726  Pos = Remainder.Pos(';'); // second segment delimiter
10727  if(Pos == 0) // no third segment so second must be a location, or cdt
10728  {
10729  Second = Remainder;
10730  if(Second == "cdt")
10731  {
10732  FormatType = TimeCmd;
10733  ShuttleLinkType = NotAShuttleLink;
10734  LocationType = AtLocation;
10735  SequenceType = Intermediate;
10736  Utilities->CallLogPop(915);
10737  return(true);
10738  }
10739  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
10740  {
10741  Utilities->CallLogPop(916);
10742  return(false);
10743  }
10744  else
10745  {
10746  FormatType = TimeLoc;
10747  LocationType = AtLocation;
10748  SequenceType = Intermediate;
10749  ShuttleLinkType = NotAShuttleLink;
10750  Utilities->CallLogPop(917);
10751  return(true);
10752  }
10753  }
10754  // here if second segment is a command, with a third & maybe fourth segments as details
10755  if((Pos != 4) && (Pos != 7) && (Pos != 8))
10756  {
10757  Utilities->CallLogPop(918);
10758  return(false);
10759  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
10760  }
10761  Second = Remainder.SubString(1, Pos - 1); // command
10762 
10763  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10764  // details
10765  Pos = Remainder.Pos(';'); // third segment delimiter
10766  if(Pos == 0)
10767  {
10768  Third = Remainder;
10769  }
10770  else
10771  {
10772  Third = Remainder.SubString(1, Pos - 1);
10773  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10774  }
10775  if((Second == "Snt") || (Second == "Snt-sh"))
10776  // third has to be 2 element idents with a space between
10777  {
10778  int SpacePos = Third.Pos(' ');
10779  if(SpacePos == 0)
10780  {
10781  Utilities->CallLogPop(919);
10782  return(false); // no space
10783  }
10784  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
10785  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
10786  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
10787  if(CheckLocationsExistInRailway)
10788  {
10789  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
10790  {
10791  Utilities->CallLogPop(920);
10792  return(false);
10793  }
10794  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
10795  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
10796  }
10797  if(Second == "Snt")
10798  {
10799  FormatType = StartNew;
10800  SequenceType = Start;
10801  LocationType = NoLocation;
10802  // can't be set until know whether located or not - done in SecondPassActions
10803  ShuttleLinkType = NotAShuttleLink;
10804  }
10805  else // Snt-sh
10806  {
10807  FormatType = SNTShuttle;
10808  LocationType = AtLocation;
10809  SequenceType = Start;
10810  ShuttleLinkType = ShuttleLink;
10811  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
10812  {
10813  Utilities->CallLogPop(1038);
10814  return(false);
10815  }
10816  }
10817  Utilities->CallLogPop(921);
10818  return(true);
10819  }
10820  if(Second == "Sns-sh") // third & fourth have to be headcodes
10821  {
10822  FormatType = SNSShuttle;
10823  LocationType = AtLocation;
10824  SequenceType = Start;
10825  ShuttleLinkType = ShuttleLink;
10826  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
10827  {
10828  Utilities->CallLogPop(1039);
10829  return(false);
10830  }
10831  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
10832  {
10833  Utilities->CallLogPop(1040);
10834  return(false);
10835  }
10836  Utilities->CallLogPop(1041);
10837  return(true);
10838  }
10839  if(Second == "F-nshs")
10840  {
10841  FormatType = FNSNonRepeatToShuttle;
10842  LocationType = AtLocation;
10843  SequenceType = Finish;
10844  ShuttleLinkType = ShuttleLink;
10845  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
10846  {
10847  Utilities->CallLogPop(1047);
10848  return(false);
10849  }
10850  Utilities->CallLogPop(1048);
10851  return(true);
10852  }
10853  if(Second == "Sns-fsh")
10854  {
10855  FormatType = SNSNonRepeatFromShuttle;
10856  LocationType = AtLocation;
10857  SequenceType = Start;
10858  ShuttleLinkType = ShuttleLink;
10859  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
10860  {
10861  Utilities->CallLogPop(1098);
10862  return(false);
10863  }
10864  Utilities->CallLogPop(1099);
10865  return(true);
10866  }
10867  if(Second == "Fns-sh") // third & fourth have to be headcodes
10868  {
10869  FormatType = FSHNewService;
10870  LocationType = AtLocation;
10871  SequenceType = Finish;
10872  ShuttleLinkType = ShuttleLink;
10873  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
10874  {
10875  Utilities->CallLogPop(1050);
10876  return(false);
10877  }
10878  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
10879  {
10880  Utilities->CallLogPop(1051);
10881  return(false);
10882  }
10883  Utilities->CallLogPop(1052);
10884  return(true);
10885  }
10886  // new segment for 'pas'
10887  if(Second == "pas") // third has to be a location
10888  {
10889  FormatType = PassTime;
10890  LocationType = EnRoute;
10891  SequenceType = Intermediate;
10892  ShuttleLinkType = NotAShuttleLink;
10893  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
10894  {
10895  Utilities->CallLogPop(1515);
10896  return(false);
10897  }
10898  Utilities->CallLogPop(1516);
10899  return(true);
10900  }
10901  // new segment for revised 'Fer'
10902  if(Second == "Fer")
10903  // third has to be a set of IDs separated by spaces, and at least 1
10904  {
10905  FormatType = ExitRailway;
10906  LocationType = EnRoute;
10907  SequenceType = Finish;
10908  ShuttleLinkType = NotAShuttleLink;
10909  if(CheckLocationsExistInRailway)
10910  {
10911  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
10912  {
10913  Utilities->CallLogPop(1519);
10914  return(false);
10915  }
10916  }
10917  Utilities->CallLogPop(1520);
10918  return(true);
10919  }
10920  // all remainder must be TimeCmdHeadCode types to be valid
10921  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
10922  (Second != "Frh-sh"))
10923  {
10924  Utilities->CallLogPop(922);
10925  return(false); // all TimeCmdHeadCode types
10926  }
10927  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
10928  {
10929  Utilities->CallLogPop(923);
10930  return(false);
10931  }
10932  FormatType = TimeCmdHeadCode;
10933  LocationType = AtLocation;
10934  if(Second == "Frh-sh")
10935  {
10936  ShuttleLinkType = ShuttleLink;
10937  }
10938  else
10939  {
10940  ShuttleLinkType = NotAShuttleLink;
10941  }
10942  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
10943  {
10944  SequenceType = Finish;
10945  }
10946  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
10947  {
10948  SequenceType = Intermediate;
10949  }
10950  if((Second == "Sfs") || (Second == "Sns"))
10951  {
10952  SequenceType = Start;
10953  }
10954  Utilities->CallLogPop(924);
10955  return(true);
10956 }
10957 
10958 // ---------------------------------------------------------------------------
10959 
10960 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
10961 {
10962  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
10963  // and contains no special characters
10964  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
10965  if(LocStr == "")
10966  {
10967  Utilities->CallLogPop(1353);
10968  return(false); // has to have at least one character
10969  }
10970  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
10971  {
10972  Utilities->CallLogPop(1354);
10973  return(false); // can't begin with a number
10974  }
10975  for(int x = 1; x < LocStr.Length() + 1; x++)
10976  {
10977  if(LocStr[x] < ' ')
10978  {
10979  Utilities->CallLogPop(1355);
10980  return(false); // contains a special character
10981  }
10982  if(LocStr[x] > 'z')
10983  {
10984  Utilities->CallLogPop(1356);
10985  return(false); // contains a character outside the standard ASCII set
10986  }
10987  }
10988  // check exists in railway location list if CheckLocationsExistInRailway is true
10989  if(CheckLocationsExistInRailway)
10990  {
10991  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
10992  {
10993  TimetableMessage(GiveMessages, "Location name '" + LocStr +
10994  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
10995  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
10996  "that includes a continuation will not be valid.");
10997  Utilities->CallLogPop(1357);
10998  return(false);
10999  }
11000  }
11001  Utilities->CallLogPop(1358);
11002  return(true);
11003 }
11004 
11005 // ---------------------------------------------------------------------------
11006 
11007 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
11008 {
11009  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
11010  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
11011  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
11012  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
11013  HeadCode);
11014  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
11015  {
11016  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
11017  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
11018  Utilities->CallLogPop(1359);
11019  return(false);
11020  }
11021  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
11022  for(int x = 1; x < (HeadCode.Length() + 1); x++)
11023  {
11024  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
11025  {
11026  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
11027  Utilities->CallLogPop(1895);
11028  return(false);
11029  }
11030  }
11031  // secondly ensure the true Headcode only has letters or digits
11032  for(int x = 3; x >= 0; x--)
11033  {
11034  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
11035  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
11036  {
11037  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
11038  Utilities->CallLogPop(1790);
11039  return(false);
11040  }
11041  }
11042  Utilities->CallLogPop(1364);
11043  return(true);
11044 }
11045 
11046 // ---------------------------------------------------------------------------
11047 
11048 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
11049 // set of legitimate track element IDs, separated by spaces, and at least 1 present
11050 {
11051  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
11052  ExitList.clear();
11053  AnsiString CurrentID = "";
11054 
11055  if(IDSet.Length() == 0)
11056  {
11057  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
11058  Utilities->CallLogPop(1521);
11059  return(false);
11060  }
11061  for(int x = 1; x <= IDSet.Length(); x++)
11062  {
11063  char C = IDSet[x];
11064  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
11065  {
11066  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
11067  Utilities->CallLogPop(1522);
11068  return(false);
11069  }
11070  }
11071  int Pos = IDSet.Pos(' '); // look for the first space
11072 
11073  while(true)
11074  {
11075  if(Pos == 0)
11076  {
11077  CurrentID = IDSet;
11078  IDSet = "";
11079  }
11080  else
11081  {
11082  CurrentID = IDSet.SubString(1, Pos - 1);
11083  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
11084  }
11085  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
11086  if(VecPos == -1)
11087  {
11088  Utilities->CallLogPop(1523);
11089  return(false); // messages given in GetTrackVectorPositionFromString
11090  }
11091  else
11092  {
11093  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
11094  {
11095  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
11096  Utilities->CallLogPop(1524);
11097  return(false);
11098  }
11099  else
11100  {
11101  // first check for duplicates
11102  if(!ExitList.empty())
11103  {
11104  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
11105  {
11106  if(*ELIT == VecPos)
11107  {
11108  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
11109  Utilities->CallLogPop(1532);
11110  return(false);
11111  }
11112  }
11113  }
11114  // of OK add it to the list
11115  ExitList.push_back(VecPos);
11116  }
11117  }
11118  if(IDSet == "")
11119  {
11120  Utilities->CallLogPop(1525);
11121  return(true);
11122  }
11123  else
11124  {
11125  Pos = IDSet.Pos(' '); // look for the next space
11126  }
11127  } // while(true)
11128 }
11129 
11130 // ---------------------------------------------------------------------------
11131 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
11132  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
11133 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
11134 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
11135 // of each item
11136 {
11137  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
11138  int Pos = 0;
11139  AnsiString Remainder = "";
11140  int SemiColonCount = 0;
11141 
11142  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
11143  {
11144  if(TrainInfoStr[x] == ';')
11145  {
11146  SemiColonCount++;
11147  }
11148  }
11149  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
11150  {
11151  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
11152  "'. Should be headcode + optional description for a continuing service;" +
11153  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
11154  Utilities->CallLogPop(880);
11155  return(false);
11156  }
11157  if(SemiColonCount == 0)
11158  {
11159  HeadCode = TrainInfoStr;
11160  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
11161  {
11162  Utilities->CallLogPop(881);
11163  return(false);
11164  }
11165  Utilities->CallLogPop(882);
11166  return(true);
11167  }
11168  if(SemiColonCount == 1) // headcode & description only
11169  {
11170  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11171  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11172  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11173  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
11174  {
11175  Utilities->CallLogPop(883);
11176  return(false);
11177  }
11178  if(Description == "")
11179  {
11180  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11181  Utilities->CallLogPop(884);
11182  return(false);
11183  }
11184  if(Description.Length() > 60)
11185  {
11186  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11187  Utilities->CallLogPop(1157);
11188  return(false);
11189  }
11190  for(int x = 1; x < Description.Length() + 1; x++)
11191  {
11192  if((Description[x] < ' ') || (Description[x] > '~'))
11193  {
11194  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11195  Utilities->CallLogPop(885);
11196  return(false);
11197  }
11198  }
11199  Utilities->CallLogPop(886);
11200  return(true);
11201  }
11202  // if here must have 6 or 7 semicolons
11203  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11204  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11205  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11206  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
11207  {
11208  Utilities->CallLogPop(887);
11209  return(false);
11210  }
11211  Pos = Remainder.Pos(';'); // 2nd delimiter
11212  Description = Remainder.SubString(1, Pos - 1);
11213  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11214  if(Description == "")
11215  {
11216  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11217  Utilities->CallLogPop(888);
11218  return(false);
11219  }
11220  if(Description.Length() > 60)
11221  {
11222  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11223  Utilities->CallLogPop(1158);
11224  return(false);
11225  }
11226  for(int x = 1; x < Description.Length() + 1; x++)
11227  {
11228  if((Description[x] < ' ') || (Description[x] > 126))
11229  {
11230  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11231  Utilities->CallLogPop(889);
11232  return(false);
11233  }
11234  }
11235  Pos = Remainder.Pos(';'); // 3rd delimiter
11236  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
11237 
11238  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11239  if(StartSpeedStr == "")
11240  {
11241  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
11242  Utilities->CallLogPop(890);
11243  return(false);
11244  }
11245  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
11246  {
11247  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
11248  {
11249  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
11250  Utilities->CallLogPop(891);
11251  return(false);
11252  }
11253  }
11254  StartSpeed = StartSpeedStr.ToInt();
11255  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11256  {
11257  StartSpeed = TTrain::MaximumSpeedLimit;
11258  if(!SSHigh) // added at v2.4.0
11259  {
11260  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11261  SSHigh = true;
11262  }
11263  }
11264  Pos = Remainder.Pos(';'); // 4th delimiter
11265  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
11266 
11267  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11268  if(MaxRunningSpeedStr == "")
11269  {
11270  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
11271  Utilities->CallLogPop(892);
11272  return(false);
11273  }
11274  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
11275  {
11276  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
11277  {
11278  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
11279  Utilities->CallLogPop(893);
11280  return(false);
11281  }
11282  }
11283  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
11284  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11285  {
11286  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
11287  if(!MRSHigh) // added at v2.4.0
11288  {
11289  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11290  MRSHigh = true;
11291  }
11292  }
11293  if(MaxRunningSpeed < 10)
11294  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11295  {
11296  MaxRunningSpeed = 10;
11297  if(!MRSLow) // added at v2.4.0
11298  {
11299  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11300  MRSLow = true;
11301  }
11302  }
11303  Pos = Remainder.Pos(';'); // 5th delimiter
11304  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
11305 
11306  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11307  if(MassStr == "")
11308  {
11309  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
11310  Utilities->CallLogPop(895);
11311  return(false);
11312  }
11313  for(int x = 1; x < MassStr.Length() + 1; x++)
11314  {
11315  if((MassStr[x] < '0') || (MassStr[x] > '9'))
11316  {
11317  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
11318  Utilities->CallLogPop(896);
11319  return(false);
11320  }
11321  }
11322  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
11323  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
11324  {
11325  Mass = TTrain::MaximumMassLimit;
11326  if(!MassHigh) // added at v2.4.0
11327  {
11328  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
11329  MassHigh = true;
11330  }
11331  }
11332  if(Mass == 0)
11333  {
11334  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
11335  Utilities->CallLogPop(897);
11336  return(false);
11337  }
11338  Pos = Remainder.Pos(';'); // 6th delimiter
11339  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
11340 
11341  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11342  if(MaxBrakeForceStr == "")
11343  {
11344  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
11345  Utilities->CallLogPop(898);
11346  return(false);
11347  }
11348  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
11349  {
11350  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
11351  {
11352  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
11353  Utilities->CallLogPop(899);
11354  return(false);
11355  }
11356  }
11357  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
11358 
11359  // convert to kg force
11360  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
11361  {
11362  MaxBrakeForce = Mass;
11363  if(!BFHigh) // added at v2.4.0
11364  {
11365  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
11366  BFHigh = true;
11367  }
11368  }
11369  if((MaxBrakeForce / Mass) < 0.01)
11370  {
11371  MaxBrakeForce = Mass * 0.01;
11372  if(!BFLow) // added at v2.4.0
11373  {
11374  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
11375  BFLow = true;
11376  }
11377  }
11378  // convert to m/s/s
11379  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
11380  // now may have just a power entry or power and signaller max. speed
11381  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
11382 
11383  if(SemiColonCount == 6)
11384  {
11385  GrossPowerStr = Remainder;
11386  SignallerSpeedStr = "30"; // default value
11387  }
11388  else // must be 7
11389  {
11390  Pos = Remainder.Pos(';'); // 7th delimiter
11391  GrossPowerStr = Remainder.SubString(1, Pos - 1);
11392  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11393  }
11394  // deal with GrossPower
11395  if(GrossPowerStr == "")
11396  {
11397  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
11398  Utilities->CallLogPop(901);
11399  return(false);
11400  }
11401  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
11402  {
11403  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
11404  {
11405  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
11406  Utilities->CallLogPop(902);
11407  return(false);
11408  }
11409  }
11410 
11411  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
11412 
11413  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
11414  {
11415  GrossPower = TTrain::MaximumPowerLimit;
11416  if(!PwrHigh)
11417  {
11418  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
11419  PwrHigh = true;
11420  }
11421  }
11422  else if(GrossPower == 0) // changed at v2.4.0
11423  {
11424  GrossPower = 0.1;
11425  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
11426  }
11427  else if((GrossPower > 0) && (GrossPower < 10000))
11428  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
11429  {
11430  GrossPower = 10000;
11431  }
11432  PowerAtRail = GrossPower * 0.8;
11433  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
11434 
11435  // deal with SignallerSpeed
11436  if(SignallerSpeedStr == "")
11437  {
11438  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
11439  Utilities->CallLogPop(1771);
11440  return(false);
11441  }
11442  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
11443  {
11444  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
11445  {
11446  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
11447  Utilities->CallLogPop(1769);
11448  return(false);
11449  }
11450  }
11451  SignallerSpeed = SignallerSpeedStr.ToInt();
11452  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
11453  {
11454  SignallerSpeed = TTrain::MaximumSpeedLimit;
11455  if(!SigSHigh)
11456  {
11457  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11458  SigSHigh = true;
11459  }
11460  }
11461  if(SignallerSpeed < 10)
11462  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11463  {
11464  SignallerSpeed = 10;
11465  if(!SigSLow)
11466  {
11467  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11468  SigSLow = true;
11469  }
11470  // Utilities->CallLogPop(1770);
11471  // return false;
11472  }
11473  Utilities->CallLogPop(904);
11474  return(true);
11475 }
11476 
11477 // ---------------------------------------------------------------------------
11478 
11479 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
11480  bool GiveMessages)
11481 {
11482  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
11483  // function checks validity of each item and returns false for error
11484  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
11485  if(OneEntry.Length() < 7)
11486  {
11487  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11488  Utilities->CallLogPop(865);
11489  return(false);
11490  }
11491  int SemiColonCount = 0;
11492 
11493  for(int x = 1; x < OneEntry.Length() + 1; x++)
11494  {
11495  if(OneEntry[x] == ';')
11496  {
11497  SemiColonCount++;
11498  }
11499  }
11500  if(SemiColonCount != 3)
11501  {
11502  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11503  Utilities->CallLogPop(866);
11504  return(false);
11505  }
11506  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
11507  {
11508  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11509  Utilities->CallLogPop(867);
11510  return(false);
11511  }
11512  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
11513  // strip off R;
11514 
11515  int Pos = 0;
11516 
11517  Pos = Remainder.Pos(';');
11518  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
11519 
11520  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11521  if(MinutesStr == "")
11522  {
11523  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
11524  Utilities->CallLogPop(868);
11525  return(false);
11526  }
11527  if(MinutesStr.Length() > 3)
11528  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
11529  {
11530  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
11531  Utilities->CallLogPop(2119);
11532  return(false);
11533  }
11534  for(int x = 1; x < MinutesStr.Length() + 1; x++)
11535  {
11536  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
11537  {
11538  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
11539  Utilities->CallLogPop(869);
11540  return(false);
11541  }
11542  }
11543  RearStartOrRepeatMins = MinutesStr.ToInt();
11544  if(RearStartOrRepeatMins == 0)
11545  {
11546  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
11547  Utilities->CallLogPop(870);
11548  return(false);
11549  }
11550  Pos = Remainder.Pos(';');
11551  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
11552 
11553  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11554  if(DigitsStr == "")
11555  {
11556  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
11557  Utilities->CallLogPop(871);
11558  return(false);
11559  }
11560  for(int x = 1; x < DigitsStr.Length() + 1; x++)
11561  {
11562  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
11563  {
11564  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
11565  Utilities->CallLogPop(872);
11566  return(false);
11567  }
11568  }
11569  if(DigitsStr.Length() > 2)
11570  {
11571  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
11572  Utilities->CallLogPop(873);
11573  return(false);
11574  }
11575  FrontStartOrRepeatDigits = DigitsStr.ToInt();
11576 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
11577  route rather than the service
11578  if(FrontStartOrRepeatDigits == 0)
11579  {
11580  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
11581  Utilities->CallLogPop(874);
11582  return false;
11583  }
11584 */
11585  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
11586  // new for v0.6b for unrestricted headcodes
11587  {
11588  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
11589  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
11590  Utilities->CallLogPop(1889);
11591  return(false);
11592  }
11593  AnsiString NumberStr = Remainder;
11594 
11595  if(NumberStr == "")
11596  {
11597  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
11598  Utilities->CallLogPop(875);
11599  return(false);
11600  }
11601  if(NumberStr.Length() > 4)
11602  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
11603  {
11604  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
11605  Utilities->CallLogPop(2118);
11606  return(false);
11607  }
11608  for(int x = 1; x < NumberStr.Length() + 1; x++)
11609  {
11610  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
11611  // catches negative numbers
11612  {
11613  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
11614  Utilities->CallLogPop(876);
11615  return(false);
11616  }
11617  }
11618  NumberOfRepeats = NumberStr.ToInt();
11619  if(NumberOfRepeats == 0)
11620  {
11621  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
11622  Utilities->CallLogPop(877);
11623  return(false);
11624  }
11625  Utilities->CallLogPop(878);
11626  return(true);
11627 }
11628 
11629 // ---------------------------------------------------------------------------
11630 
11631 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
11632 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
11633  vector rather than the timetable
11634  Note also that for unlocated Snt entries the LocationType hasn't yet been set
11635 
11636  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
11637 
11638  For info:-
11639  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
11640  {
11641  public:
11642  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled action entries, null
11644  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
11645  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
11646  int NumberOfRepeats; ///< the number of repeating services
11647  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
11649  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
11651  TExitList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
11652  TTimetableFormatType FormatType; ///< defines the timetable action type
11653  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
11654  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
11655  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
11656  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
11658  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
11660 
11661  // inline function
11662 
11664  TActionVectorEntry() {
11665  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
11666  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
11667  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
11668  Warning = false; SignallerControl = false;
11669  }
11670  };
11671 
11672  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
11673 
11674  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
11675  {
11676  public:
11677  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
11680  double MaxBrakeRate; ///< in metres/sec/sec
11681  double MaxRunningSpeed; ///< in km/h
11682  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
11683  int Mass; ///< in kg
11684  int NumberOfTrains; ///< number of repeats + 1
11685  int SignallerSpeed; ///< in km/h for use when under signaller control
11686  int StartSpeed; ///< in km/h
11687  TActionVector ActionVector; ///< all the actions for the train
11688  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
11689 
11690  //inline function
11691 
11693  TTrainDataEntry()
11694  {
11695  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
11696  }
11697  };
11698 
11699  Allowable successors:-
11700  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
11701  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11702  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11703  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11704  set location, else fails)
11705  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11706  set location, else fails)
11707  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11708  set location, else fails)
11709  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11710  set location, else fails)
11711  Fns -> R only
11712  F-nshs -> Nothing (no repeats permitted)
11713  Fjo -> R only
11714  Frh -> R only
11715  Fer -> R only
11716  Frh-sh -> R only
11717  Fns-sh -> R only
11718  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
11719  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11720  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11721  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11722  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11723  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11724  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11725  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11726  Repeat -> Nothing
11727 
11728  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
11729  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
11730  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
11731  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
11732  Check all times increase or stay same through ActionVector
11733  Cycle through all entries in vector setting arr & dep times based on above list
11734  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
11735  Check locations match the arr & dep TimeLoc entries
11736  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
11737  Make above valid succession checks
11738  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
11739  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
11740  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
11741  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
11742  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
11743  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
11744  Set train info for Sfs & Sns entries
11745  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
11746  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
11747  element at each end, or length of 3 & 1 extra element at either end
11748  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
11749  Check all Cmds have EventTime set & Arr & Dep times = -1
11750  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
11751  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
11752 
11753  Give messages in function if errors detected and clear the vector. Return false for failure.
11754 */
11755 
11756 /* Earlier checks:-
11757  Checks carried out with error messages in this function:-
11758  At least one comma in the line (it's based on a csv file);
11759  No entries following train information;
11760  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11761  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11762  First entry not a start entry;
11763  Train information incomplete before a start entry;
11764  Entry follows a finish entry but doesn't begin with 'R';
11765  SplitEntry returns false in a finish entry - message repeats the entry for information;
11766  Last action entry isn't a finish entry.
11767 
11768  Function returns false with no message if:-
11769  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
11770  time is found at all then an error message is given in the calling function);
11771  SplitTrainInfo returns false (message given in called function);
11772  SplitRepeat returns false (message given in called function).
11773 */{
11774  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
11775  if(TrainDataVector.empty())
11776  {
11777  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
11778  TrainDataVector.clear();
11779  Utilities->CallLogPop(1832);
11780  return(false);
11781  }
11782 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
11783  1) must have at least one actionvector entry
11784  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
11785  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
11786  4) first entry must be a start;
11787  4a) if first entry is Snt and not signallercontrol and second is a finish then it can only be Frh, Fjo or Fer//added for v2.0.0
11788  5) a start must be the first entry;
11789  6) a repeat entry must be the last;
11790  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
11791  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
11792  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
11793 */
11794 
11795  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
11796  TwoLocationFlag = false; //added at v2.9.1
11797  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
11798  {
11799  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11800  if(TrainDataVector.at(x).ActionVector.empty())
11801  {
11802  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
11803  TrainDataVector.clear();
11804  Utilities->CallLogPop(1833);
11805  return(false);
11806  }
11807  }
11808  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
11809  {
11810  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11811  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11812  if(!(AVEntry0.SignallerControl))
11813  {
11814  if(TrainDataVector.at(x).ActionVector.size() == 1)
11815  {
11816  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
11817  TrainDataVector.clear();
11818  Utilities->CallLogPop(1822);
11819  return(false);
11820  }
11821  }
11822  }
11823  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
11824  {
11825  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11826  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11827  if(AVEntry0.SignallerControl)
11828  {
11829  if(TrainDataVector.at(x).ActionVector.size() > 2)
11830  {
11831  SecondPassMessage(GiveMessages,
11832  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
11833  TDEntry.HeadCode);
11834  TrainDataVector.clear();
11835  Utilities->CallLogPop(1837);
11836  return(false);
11837  }
11838  if(TrainDataVector.at(x).ActionVector.size() > 1)
11839  {
11840  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11841  if(AVEntry1.FormatType != Repeat)
11842  {
11843  SecondPassMessage(GiveMessages,
11844  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
11845  TrainDataVector.clear();
11846  Utilities->CallLogPop(1838);
11847  return(false);
11848  }
11849  }
11850  }
11851  }
11852  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
11853  {
11854  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11855  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11856  if(AVEntry0.SequenceType != Start)
11857  {
11858  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
11859  TrainDataVector.clear();
11860  Utilities->CallLogPop(1824);
11861  return(false);
11862  }
11863  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
11864  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
11865  // and others for a located Snt, but those checks done later
11866  {
11867  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11868  // must be a second entry if first not signallercontrol
11869  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
11870  {
11871  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
11872  TDEntry.HeadCode);
11873  TrainDataVector.clear();
11874  Utilities->CallLogPop(2046);
11875  return(false);
11876  }
11877  }
11878  }
11879  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
11880  {
11881  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11882  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11883  {
11884  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11885  if((AVEntry.SequenceType == Start) && (y != 0))
11886  {
11887  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
11888  TrainDataVector.clear();
11889  Utilities->CallLogPop(1825);
11890  return(false);
11891  }
11892  }
11893  }
11894  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
11895  {
11896  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11897  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11898  {
11899  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11900  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
11901  {
11902  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
11903  TrainDataVector.clear();
11904  Utilities->CallLogPop(1826);
11905  return(false);
11906  }
11907  }
11908  }
11909  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
11910  {
11911  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11912  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11913  {
11914  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11915  if((y == 0) && AVEntry.SignallerControl)
11916  {
11917  break;
11918  }
11919  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
11920  {
11921  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
11922  {
11923  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
11924  TrainDataVector.clear();
11925  Utilities->CallLogPop(1827);
11926  return(false);
11927  }
11928  if(AVEntry.FormatType == Repeat)
11929  {
11930  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
11931  if(LastAVEntry.SequenceType != Finish)
11932  {
11933  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
11934  TrainDataVector.clear();
11935  Utilities->CallLogPop(1828);
11936  return(false);
11937  }
11938  }
11939  }
11940  }
11941  }
11942  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
11943  {
11944  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11945  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11946  {
11947  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11948  if(AVEntry.SequenceType == Finish)
11949  {
11950  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
11951  {
11952  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
11953  TrainDataVector.clear();
11954  Utilities->CallLogPop(1829);
11955  return(false);
11956  }
11957  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
11958  {
11959  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
11960  {
11961  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
11962  TDEntry.HeadCode);
11963  TrainDataVector.clear();
11964  Utilities->CallLogPop(1830);
11965  return(false);
11966  }
11967  }
11968  }
11969  }
11970  }
11971 
11972  // end of new preliminary checks
11973 
11974  // check ActionVector present and check start event successor validity
11975  // For Snt & Snt-sh set location if stopped, don't set any times yet
11976  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11977  {
11978  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11979  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11980  // use reference so can change internals where necessary
11981  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
11982  {
11983  AnsiString LocationName = "";
11984  if(IsSNTEntryLocated(0, TDEntry, LocationName))
11985  // it is at a location
11986  {
11987  if(TDEntry.StartSpeed == 0) // stopped
11988  {
11989  AVEntry0.LocationName = LocationName;
11990  AVEntry0.LocationType = AtLocation;
11991  // check successor validity for located Snt that isn't a SignallerControl entry
11992  if(!AVEntry0.SignallerControl)
11993  {
11994  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11995  // at least 2 entries present checked in integrity check so (1) valid
11996  if(!AtLocSuccessor(AVEntry1))
11997  {
11998  // Frh following Snt-sh will return false in location check, so no need to check here
11999  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
12000  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12001  TrainDataVector.clear();
12002  Utilities->CallLogPop(523);
12003  return(false);
12004  }
12005  }
12006  }
12007  else
12008  {
12009  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
12010  TDEntry.HeadCode);
12011  TrainDataVector.clear();
12012  Utilities->CallLogPop(791);
12013  return(false);
12014  }
12015  }
12016  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
12017  {
12018  if(AVEntry0.Command == "Snt-sh")
12019  {
12020  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
12021  TrainDataVector.clear();
12022  Utilities->CallLogPop(1042);
12023  return(false);
12024  }
12025  AVEntry0.LocationType = EnRoute;
12026  if(!AVEntry0.SignallerControl)
12027  {
12028  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12029  // at least 2 entries checked in integrity check so (1) valid
12030  if(!MovingSuccessor(AVEntry1))
12031  {
12032  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
12033  TDEntry.HeadCode);
12034  TrainDataVector.clear();
12035  Utilities->CallLogPop(790);
12036  return(false);
12037  }
12038  }
12039  }
12040  }
12041  // check other start successors
12042  else if(AVEntry0.SequenceType == Start)
12043  {
12044  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12045  // at least 2 entries present checked in integrity check so (1) valid
12046  if(!AtLocSuccessor(AVEntry1))
12047  {
12048  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
12049  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12050  TrainDataVector.clear();
12051  Utilities->CallLogPop(793);
12052  return(false);
12053  }
12054  }
12055  }
12056 
12057  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
12058  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12059  {
12060  bool FoundFlag = false;
12061  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12062  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
12063  // use reference so can change internals
12064  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
12065  {
12066  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
12067  {
12068  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
12069  if(AVEntry2.FormatType == TimeLoc)
12070  {
12071  FoundFlag = true;
12072  AVEntry.LocationName = AVEntry2.LocationName;
12073  break;
12074  }
12075  }
12076  if(!FoundFlag)
12077  {
12078  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
12079  TDEntry.HeadCode);
12080  TrainDataVector.clear();
12081  Utilities->CallLogPop(851);
12082  return(false);
12083  }
12084  }
12085  }
12086 
12087  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
12088  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12089  {
12090  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12091  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12092  {
12093  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12094  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
12095  {
12096  if(AVEntry.LocationName == "")
12097  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
12098  {
12099  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
12100  ": an event should have had a location name associated with it but it could not be found");
12101  TrainDataVector.clear();
12102  Utilities->CallLogPop(1831);
12103  return(false);
12104  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
12105  }
12106  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
12107  {
12108  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
12109  // use reference so can change internals where necessary
12110  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
12111  {
12112  AVEntry2.LocationName = AVEntry.LocationName;
12113  }
12114  else
12115  {
12116  break;
12117  }
12118  }
12119  }
12120  }
12121  }
12122  // all location names now set
12123 
12124  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
12125  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12126  {
12127  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12128  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12129  {
12130  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12131  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
12132  {
12133  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
12134  // i.e at least one more, must be a repeat
12135  {
12136  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12137  {
12138  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
12139  TrainDataVector.clear();
12140  Utilities->CallLogPop(798);
12141  return(false);
12142  }
12143  }
12144  }
12145  if(AVEntry.Command == "F-nshs")
12146  {
12147  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12148  // i.e has to be the last
12149  {
12150  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
12151  TrainDataVector.clear();
12152  Utilities->CallLogPop(1049);
12153  return(false);
12154  }
12155  }
12156  if(AVEntry.Command == "pas")
12157  {
12158  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12159  {
12160  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
12161  TrainDataVector.clear();
12162  Utilities->CallLogPop(1518);
12163  return(false);
12164  }
12165  }
12166  if(AVEntry.Command == "jbo")
12167  {
12168  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12169  {
12170  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
12171  TrainDataVector.clear();
12172  Utilities->CallLogPop(800);
12173  return(false);
12174  }
12175  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12176  if(!AtLocSuccessor(AVEntry2))
12177  {
12178  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
12179  ". The event isn't valid for a stationary train.");
12180  TrainDataVector.clear();
12181  Utilities->CallLogPop(801);
12182  return(false);
12183  }
12184  }
12185  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
12186  {
12187  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12188  {
12189  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
12190  TrainDataVector.clear();
12191  Utilities->CallLogPop(802);
12192  return(false);
12193  }
12194  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12195  if(!AtLocSuccessor(AVEntry2))
12196  {
12197  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
12198  ". The event isn't valid for a stationary train.");
12199  TrainDataVector.clear();
12200  Utilities->CallLogPop(803);
12201  return(false);
12202  }
12203  }
12204  if(AVEntry.Command == "cdt")
12205  {
12206  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12207  {
12208  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
12209  TrainDataVector.clear();
12210  Utilities->CallLogPop(804);
12211  return(false);
12212  }
12213  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12214  if(!AtLocSuccessor(AVEntry2))
12215  {
12216  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
12217  ". The event isn't valid for a stationary train.");
12218  TrainDataVector.clear();
12219  Utilities->CallLogPop(805);
12220  return(false);
12221  }
12222  }
12223  if(AVEntry.FormatType == TimeTimeLoc)
12224  {
12225  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12226  {
12227  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
12228  TrainDataVector.clear();
12229  Utilities->CallLogPop(806);
12230  return(false);
12231  }
12232  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12233  if(!MovingSuccessor(AVEntry2))
12234  {
12235  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
12236  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
12237  TrainDataVector.clear();
12238  Utilities->CallLogPop(807);
12239  return(false);
12240  }
12241  }
12242  if(AVEntry.FormatType == PassTime)
12243  {
12244  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12245  {
12246  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
12247  TrainDataVector.clear();
12248  Utilities->CallLogPop(1530);
12249  return(false);
12250  }
12251  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12252  if(!MovingSuccessor(AVEntry2))
12253  {
12254  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
12255  ". The event isn't valid for a moving train.");
12256  TrainDataVector.clear();
12257  Utilities->CallLogPop(1531);
12258  return(false);
12259  }
12260  }
12261  if(AVEntry.FormatType == Repeat)
12262  {
12263  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12264  {
12265  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
12266  TrainDataVector.clear();
12267  Utilities->CallLogPop(808);
12268  return(false);
12269  }
12270  }
12271  }
12272  }
12273 
12274  // set arrival & departure times for TimeLocs & set their EventTimes to -1
12275  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12276  {
12277  bool LastEntryIsAnArrival = false;
12278  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12279  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
12280  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12281  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12282  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
12283  {
12284  LastEntryIsAnArrival = false;
12285  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12286  {
12287  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12288  if(AVEntry.FormatType == TimeLoc)
12289  {
12290  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12291  {
12292  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12293  }
12294  if(LastEntryIsAnArrival)
12295  {
12296  AVEntry.DepartureTime = AVEntry.EventTime;
12297  AVEntry.EventTime = TDateTime(-1);
12298  LastEntryIsAnArrival = false;
12299  }
12300  else // last entry a departure
12301  {
12302  AVEntry.ArrivalTime = AVEntry.EventTime;
12303  AVEntry.EventTime = TDateTime(-1);
12304  LastEntryIsAnArrival = true;
12305  }
12306  }
12307  }
12308  }
12309  else // all others stopped at beginning
12310  {
12311  LastEntryIsAnArrival = true;
12312  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12313  {
12314  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12315  if(AVEntry.FormatType == TimeLoc)
12316  {
12317  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12318  {
12319  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12320  }
12321  if(LastEntryIsAnArrival)
12322  {
12323  AVEntry.DepartureTime = AVEntry.EventTime;
12324  AVEntry.EventTime = TDateTime(-1);
12325  LastEntryIsAnArrival = false;
12326  }
12327  else // last entry a departure
12328  {
12329  AVEntry.ArrivalTime = AVEntry.EventTime;
12330  AVEntry.EventTime = TDateTime(-1);
12331  LastEntryIsAnArrival = true;
12332  }
12333  }
12334  }
12335  }
12336  }
12337  // perform remaining successor checks for TimeLocs
12338  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12339  {
12340  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12341  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12342  {
12343  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12344  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
12345  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
12346  {
12347  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12348  {
12349  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
12350  TrainDataVector.clear();
12351  Utilities->CallLogPop(809);
12352  return(false);
12353  }
12354  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12355  if(!AtLocSuccessor(AVEntry2))
12356  {
12357  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
12358  ". The event isn't valid for a stationary train.");
12359  TrainDataVector.clear();
12360  Utilities->CallLogPop(810);
12361  return(false);
12362  }
12363  }
12364  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
12365  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
12366  {
12367  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12368  {
12369  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
12370  TrainDataVector.clear();
12371  Utilities->CallLogPop(811);
12372  return(false);
12373  }
12374  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12375  if(!MovingSuccessor(AVEntry2))
12376  {
12377  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
12378  ". The event isn't valid for a moving train.");
12379  TrainDataVector.clear();
12380  Utilities->CallLogPop(812);
12381  return(false);
12382  }
12383  }
12384  }
12385  }
12386 
12387  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
12388  // & repeats have no times set
12389  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12390  {
12391  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12392  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12393  {
12394  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12395  if(AVEntry.FormatType == TimeLoc)
12396  {
12397  if(AVEntry.EventTime != TDateTime(-1))
12398  {
12399  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12400  }
12401  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
12402  {
12403  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
12404  }
12405  }
12406  if(AVEntry.FormatType == TimeTimeLoc)
12407  {
12408  if(AVEntry.EventTime != TDateTime(-1))
12409  {
12410  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12411  }
12412  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
12413  {
12414  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
12415  }
12416  }
12417  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
12418  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
12419  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
12420  {
12421  if(AVEntry.EventTime == TDateTime(-1))
12422  {
12423  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
12424  }
12425  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12426  {
12427  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
12428  }
12429  }
12430  if(AVEntry.FormatType == Repeat)
12431  {
12432  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12433  {
12434  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
12435  }
12436  }
12437  }
12438  }
12439 
12440  // check times stay same or increase, note that can have time of 0 if include midnight
12441  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12442  {
12443  TDateTime CurrentTime = TTClockTime; // the timetable start time
12444  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12445  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12446  {
12447  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12448  if(AVEntry.FormatType == Repeat)
12449  {
12450  break;
12451  }
12452  if(AVEntry.FormatType == FinRemHere)
12453  {
12454  break;
12455  }
12456  if(AVEntry.FormatType == TimeTimeLoc)
12457  {
12458  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
12459  {
12460  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
12461  TDEntry.HeadCode);
12462  TrainDataVector.clear();
12463  Utilities->CallLogPop(813);
12464  return(false);
12465  }
12466  if(AVEntry.ArrivalTime < CurrentTime)
12467  {
12468  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
12469  TDEntry.HeadCode);
12470  TrainDataVector.clear();
12471  Utilities->CallLogPop(814);
12472  return(false);
12473  }
12474  CurrentTime = AVEntry.DepartureTime;
12475  continue;
12476  }
12477  if(AVEntry.FormatType == TimeLoc)
12478  {
12479  if(AVEntry.ArrivalTime >= TDateTime(0))
12480  {
12481  if(AVEntry.ArrivalTime < CurrentTime)
12482  {
12483  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12484  TrainDataVector.clear();
12485  Utilities->CallLogPop(815);
12486  return(false);
12487  }
12488  CurrentTime = AVEntry.ArrivalTime;
12489  }
12490  else
12491  {
12492  if(AVEntry.DepartureTime < CurrentTime)
12493  // both may be 0 legitimately so must allow for this
12494  {
12495  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12496  TrainDataVector.clear();
12497  Utilities->CallLogPop(816);
12498  return(false);
12499  }
12500  CurrentTime = AVEntry.DepartureTime;
12501  }
12502  continue;
12503  }
12504  if(AVEntry.EventTime < CurrentTime)
12505  // all others have EventTime set
12506  {
12507  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
12508  ", may be before timetable start time");
12509  TrainDataVector.clear();
12510  Utilities->CallLogPop(835);
12511  return(false);
12512  }
12513  CurrentTime = AVEntry.EventTime;
12514  continue;
12515  }
12516  }
12517 
12518  // check locations consistent
12519  AnsiString LastLocationName = "";
12520 
12521  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12522  {
12523  bool LastEntryIsAnArrival = false;
12524  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12525  // first deal with moving Snt entries (all else stopped)
12526  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
12527  {
12528  LastEntryIsAnArrival = false;
12529  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
12530  if(LastLocationName != "")
12531  {
12532  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
12533  }
12534  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
12535  y++) // note that immediate successor to a moving Snt can only be a Moving type
12536  {
12537  // if it's a SignallerControl entry then the condition isn't met
12538  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12539  if(AVEntry.FormatType == Repeat)
12540  {
12541  break; // repeat = reached end (+allows repeat after signaller controlled entry)
12542  }
12543  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12544  {
12545  if(AVEntry.LocationName != LastLocationName)
12546  {
12547  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12548  AVEntry.Command);
12549  TrainDataVector.clear();
12550  Utilities->CallLogPop(823);
12551  return(false);
12552  }
12553  }
12554  else if(AVEntry.FormatType == TimeCmd)
12555  // cdt is the only TimeCmd
12556  {
12557  if(AVEntry.LocationName != LastLocationName)
12558  {
12559  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12560  AVEntry.Command);
12561  TrainDataVector.clear();
12562  Utilities->CallLogPop(824);
12563  return(false);
12564  }
12565  }
12566  else if(AVEntry.FormatType == TimeTimeLoc)
12567  {
12568  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12569  // last entry must be a departure or would have failed earlier
12570  {
12571  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12572  TwoLocationFlag = true;
12573 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12574 // TwoOrMoreLocationsWarningGiven = true;
12575 
12576 
12577  }
12578  LastLocationName = AVEntry.LocationName;
12579  LastEntryIsAnArrival = false;
12580  }
12581  else if(AVEntry.FormatType == TimeLoc)
12582  {
12583  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12584  {
12585  SecondPassMessage(GiveMessages,
12586  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12587  TrainDataVector.clear();
12588  Utilities->CallLogPop(826);
12589  return(false);
12590  }
12591  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
12592  {
12593  SecondPassMessage(GiveMessages,
12594  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
12595  TrainDataVector.clear();
12596  Utilities->CallLogPop(827);
12597  return(false);
12598  }
12599  LastLocationName = AVEntry.LocationName;
12600  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12601  }
12602  }
12603  }
12604  else // all stationary starting entries
12605  {
12606  LastEntryIsAnArrival = true;
12607  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
12608  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12609  {
12610  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12611  if(AVEntry.FormatType == Repeat)
12612  {
12613  break;
12614  }
12615  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12616  // no need to add anything for shuttle starts since they are at loc (0) anyway
12617  {
12618  if(AVEntry.LocationName != LastLocationName)
12619  {
12620  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12621  AVEntry.Command);
12622  TrainDataVector.clear();
12623  Utilities->CallLogPop(828);
12624  return(false);
12625  }
12626  }
12627  else if(AVEntry.FormatType == TimeCmd)
12628  // cdt is the only TimeCmd
12629  {
12630  if(AVEntry.LocationName != LastLocationName)
12631  {
12632  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12633  AVEntry.Command);
12634  TrainDataVector.clear();
12635  Utilities->CallLogPop(829);
12636  return(false);
12637  }
12638  }
12639  else if(AVEntry.FormatType == TimeTimeLoc)
12640  {
12641  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12642  // last entry must be a departure or would have failed earlier
12643  {
12644  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12645  TwoLocationFlag = true;
12646 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12647 // TwoOrMoreLocationsWarningGiven = true;
12648  }
12649  LastLocationName = AVEntry.LocationName;
12650  LastEntryIsAnArrival = false;
12651  }
12652  else if(AVEntry.FormatType == TimeLoc)
12653  {
12654  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12655  {
12656  SecondPassMessage(GiveMessages,
12657  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12658  TrainDataVector.clear();
12659  Utilities->CallLogPop(831);
12660  return(false);
12661  }
12662  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
12663  {
12664  SecondPassMessage(GiveMessages,
12665  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
12667 // TrainDataVector.clear();
12668 // Utilities->CallLogPop(832);
12669 // return false;
12670  }
12671  LastLocationName = AVEntry.LocationName;
12672  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12673  }
12674  }
12675  }
12676  }
12677 
12678  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
12679  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
12680  AnsiString LocationNameToBeChecked = "";
12681 
12682  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12683  {
12684  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12685  unsigned int y = 0;
12686  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
12687  // first discard unlocated Snt entries as they don't have location name set
12688  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12689  {
12690  y = 1;
12691  }
12692  while(y < TDEntry.ActionVector.size())
12693  // need to check each location name separately in turn, skipped for SignallerControl entries
12694  // if y == 1
12695  {
12696  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
12697  {
12698  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12699  }
12700  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
12701  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
12702  {
12703  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
12704  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
12705  {
12706  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12707  }
12708  if(AVEntry.Command == "cdt")
12709  {
12710  break; // out of the 'z' loop since the check is only valid up to a change of direction
12711  }
12712  if(AVEntry.LocationName == LocationNameToBeChecked)
12713  {
12714  continue; // keep going while name same
12715  }
12716  if(AVEntry.LocationName != LocationNameToBeChecked)
12717  // if name different check forwards to see if repeats
12718  {
12719  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
12720  {
12721  if(TDEntry.ActionVector.at(a).Command == "cdt")
12722  {
12723  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
12724  }
12725  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12726  {
12727  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12728  TwoLocationFlag = true;
12729 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12730 // TwoOrMoreLocationsWarningGiven = true;
12731  }
12732  }
12733  break; // out of the 'z' loop since have checked 'a' as far as need to
12734  }
12735  }
12736  y++;
12737  }
12738  }
12739 
12740  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
12741  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12742  {
12743  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12744  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12745  {
12746  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12747  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
12748  {
12749  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
12750  }
12751  AnsiString LocName = "";
12752  // dummy, only used so can call IsSNTEntryLocated
12753  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
12754  {
12755  if(AVEntry.LocationName == "")
12756  {
12757  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
12758  }
12759  }
12760  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
12761  {
12762  if(AVEntry.LocationName != "")
12763  {
12764  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
12765  }
12766  }
12767  }
12768  }
12769 
12770 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12771  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12772  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
12773 
12774  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
12775  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
12776 
12777  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
12778  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
12779  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
12780 */
12781  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
12782  {
12783  // non-shuttles & non-repeating links separately, but don't check that there isn't a
12784  // duplicate between a non-repeating shuttle and another - leave original tests in as
12785  // these also set the pointers
12786  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12787  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12788  {
12789  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12790  if(AVEntry.OtherHeadCode != "")
12791  {
12792  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
12793  {
12794  Utilities->CallLogPop(1584);
12795  return(false); // error message given in called function
12796  }
12797  }
12798  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12799  {
12800  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
12801  {
12802  Utilities->CallLogPop(1585);
12803  return(false); // error message given in called function
12804  }
12805  }
12806  }
12807  }
12808 
12809  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12810  {
12811  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12812  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12813  {
12814  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12815  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12816  {
12817  if(AVEntry.OtherHeadCode != "")
12818  {
12819  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
12820  // false = non-shuttle
12821  {
12822  Utilities->CallLogPop(864);
12823  return(false); // error message given in called function
12824  }
12825  }
12826  }
12827  }
12828  }
12829 
12830  // now repeat the check just for the shuttles
12831  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12832  {
12833  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12834  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12835  {
12836  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12837  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
12838  {
12839  if(AVEntry.OtherHeadCode != "")
12840  {
12841  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
12842  // true = shuttle
12843  {
12844  Utilities->CallLogPop(1100);
12845  return(false); // error message given in called function
12846  }
12847  }
12848  }
12849  }
12850  }
12851 
12852  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
12853  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12854  {
12855  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12856  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12857  {
12858  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12859  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12860  {
12862  {
12863  Utilities->CallLogPop(1060);
12864  return(false); // error message given in called function
12865  }
12866  }
12867  }
12868  }
12869 
12870  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
12871  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
12872  // don't ever need to and as designed would skip repeats
12873  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12874  {
12875  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12876  {
12877  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12878  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
12879  {
12880  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
12881  {
12882  Utilities->CallLogPop(1090);
12883  return(false); // error message given in called function
12884  }
12885  }
12886  }
12887  }
12888 
12889  // check all entries have all types set to something
12890  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12891  {
12892  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12893  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12894  {
12895  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12896  if(AVEntry.FormatType == NoFormat)
12897  {
12898  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
12899  }
12900  else if(AVEntry.SequenceType == NoSequence)
12901  {
12902  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
12903  }
12904  else if(AVEntry.LocationType == NoLocation)
12905  {
12906  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
12907  }
12908  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
12909  {
12910  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
12911  }
12912  }
12913  }
12914 
12915  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
12916  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12917  {
12918  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12919  // non-const reference so can alter content
12920  TTrainOperatingData TData;
12921  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
12922  if(LastAVEntry.FormatType == Repeat) // check if a repeat
12923  {
12924 /*
12925  class TTrainOperatingData
12926  {
12927  public:
12928  int TrainID; - default, set at construction
12929  TActionEventType EventReported; used during operation
12930  TRunningEntry RunningEntry; - default, set at construction
12931  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
12932  };
12933 */
12934  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
12935  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
12936  {
12937  TDEntry.TrainOperatingDataVector.push_back(TData);
12938  }
12939  }
12940  else
12941  {
12942  TDEntry.NumberOfTrains = 1;
12943  }
12944  }
12945 
12946  // check that don't include any Continuation names
12947  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12948  {
12949  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12950  {
12951  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
12952  AnsiString HC = TrainDataVector.at(x).HeadCode;
12953  if(LocName != "")
12954  {
12955  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
12956  {
12957  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
12958  TrainDataVector.clear();
12959  Utilities->CallLogPop(1578);
12960  return(false);
12961  }
12962  }
12963  }
12964  }
12965 
12966  // check that all repeat times below 96h
12967  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12968  {
12969  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
12970  int IncMinutes = 0;
12971  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
12972  {
12973  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
12974  }
12975  else
12976  {
12977  continue; // basic times already checked in CheckTimeValidity
12978  }
12979  AnsiString HC = TrainDataVector.at(x).HeadCode;
12980  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12981  {
12982  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
12983  {
12984  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12985  {
12986  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
12987  TrainDataVector.clear();
12988  Utilities->CallLogPop(1818);
12989  return(false);
12990  }
12991  }
12992  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
12993  {
12994  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12995  // 3d 23h 59m = 3.9993055556
12996  {
12997  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12998  TrainDataVector.clear();
12999  Utilities->CallLogPop(1819);
13000  return(false);
13001  }
13002  }
13003  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
13004  {
13005  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13006  // 3d 23h 59m = 3.9993055556
13007  {
13008  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13009  TrainDataVector.clear();
13010  Utilities->CallLogPop(1820);
13011  return(false);
13012  }
13013  }
13014  }
13015  }
13016 
13017  // Now that all set up change any extended headcodes back to ordinary headcodes
13018  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13019  {
13020  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
13021  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13022  {
13023  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
13024  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
13025  }
13026  }
13027 
13028  // SaveTrainDataVectorToFile(0);//test
13030  Utilities->CallLogPop(782);
13031  return(true);
13032 }
13033 
13034 // ---------------------------------------------------------------------------
13035 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
13037 {
13038  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
13039 }
13040 
13041 // ---------------------------------------------------------------------------
13042 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
13044 {
13045  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
13046  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
13047  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
13048 }
13049 
13050 // ---------------------------------------------------------------------------
13051 
13052 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
13053 {
13054  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
13055  if(HeadCode.Length() > 4) // ignore otherwise
13056  {
13057  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
13058  }
13059  Utilities->CallLogPop(1593);
13060 }
13061 
13062 // ---------------------------------------------------------------------------
13063 
13064 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
13065 {
13066  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
13067  SecondHeadCode);
13068  int ForwardCount = 0;
13069  int ReverseCount = 0;
13070 
13071  if(MainHeadCode == SecondHeadCode)
13072  {
13073  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
13074  TrainDataVector.clear();
13075  Utilities->CallLogPop(1594);
13076  return(false);
13077  }
13078  // forward check
13079  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13080  {
13081  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13082  if(TDEntry.HeadCode == MainHeadCode)
13083  {
13084  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13085  {
13086  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13087  if(AVEntry.OtherHeadCode == SecondHeadCode)
13088  {
13089  ForwardCount++;
13090  }
13091  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
13092  // need own check in case both 'Other' & 'NonRepeating' have same headcode
13093  {
13094  ForwardCount++;
13095  }
13096  }
13097  }
13098  }
13099  if(ForwardCount == 0)
13100  // this is an exception because the headcodes are selected in the same order as the forward check
13101  {
13102  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
13103  }
13104  if(ForwardCount > 2)
13105  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
13106  {
13107  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
13108  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13109  TrainDataVector.clear();
13110  Utilities->CallLogPop(1587);
13111  return(false);
13112  }
13113  // reverse check
13114  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13115  {
13116  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13117  if(TDEntry.HeadCode == SecondHeadCode)
13118  {
13119  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13120  {
13121  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13122  if(AVEntry.OtherHeadCode == MainHeadCode)
13123  {
13124  ReverseCount++;
13125  }
13126  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13127  {
13128  ReverseCount++;
13129  }
13130  }
13131  }
13132  }
13133 
13134  if(ReverseCount == 0)
13135  {
13136  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
13137  TrainDataVector.clear();
13138  Utilities->CallLogPop(1588);
13139  return(false);
13140  }
13141  if(ReverseCount > 2)
13142  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
13143  {
13144  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
13145  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13146  TrainDataVector.clear();
13147  Utilities->CallLogPop(1589);
13148  return(false);
13149  }
13150  if(ForwardCount != ReverseCount)
13151  {
13152  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
13153  " than the other way round");
13154  TrainDataVector.clear();
13155  Utilities->CallLogPop(1610);
13156  return(false);
13157  }
13158  Utilities->CallLogPop(1590);
13159  return(true);
13160 }
13161 
13162 // ---------------------------------------------------------------------------
13163 
13164 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
13165 /* Return false for no find or more than one find, check correct types of link
13166  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
13167  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
13168  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
13169  Then do the same in reverse.
13170  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
13171  if main is Fns other must be Sns; if main is jbo other must be Fjo.
13172  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
13173  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
13174  for Sfs & Sns services. Finally check the repeat entries if present are consistent
13175 
13176  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
13177  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
13178 
13179  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
13180 */
13181 
13182 {
13183  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
13184  int ForwardCount = 0;
13185  int ReverseCount = 0;
13186  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13187  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13188  TTrainDataEntry *MainTrainDataPtr = 0;
13189  TTrainDataEntry *OtherTrainDataPtr = 0;
13190 
13191  // forward check
13192  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13193  {
13194  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13195  if(TDEntry.HeadCode == MainHeadCode)
13196  {
13197  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13198  {
13199  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13200  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13201  {
13202  if(AVEntry.OtherHeadCode == OtherHeadCode)
13203  {
13204  MainTrainDataPtr = &TrainDataVector.at(x);
13205  ForwardEntryPtr = &AVEntry;
13206  ForwardCount++;
13207  ForwardTDVectorNumber = x;
13208  }
13209  }
13210  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
13211  (AVEntry.Command == "Frh-sh")))
13212  {
13213  if(AVEntry.OtherHeadCode == OtherHeadCode)
13214  {
13215  MainTrainDataPtr = &TrainDataVector.at(x);
13216  ForwardEntryPtr = &AVEntry;
13217  ForwardCount++;
13218  ForwardTDVectorNumber = x;
13219  }
13220  }
13221  }
13222  }
13223  }
13224  if(ForwardCount == 0)
13225  // this is an exception because the headcodes are selected in the same order as the forward check
13226  {
13227  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
13228  }
13229  if(ForwardCount > 1)
13230  {
13231  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
13232  MainHeadCode);
13233  TrainDataVector.clear();
13234  Utilities->CallLogPop(836);
13235  return(false);
13236  }
13237  // reverse check
13238  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13239  {
13240  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13241  if(TDEntry.HeadCode == OtherHeadCode)
13242  {
13243  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13244  {
13245  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13246  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13247  {
13248  if(AVEntry.OtherHeadCode == MainHeadCode)
13249  {
13250  OtherTrainDataPtr = &TrainDataVector.at(x);
13251  ReverseCount++;
13252  ReverseEntryPtr = &AVEntry;
13253  ReverseTDVectorNumber = x;
13254  }
13255  }
13256  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
13257  {
13258  if(AVEntry.OtherHeadCode == MainHeadCode)
13259  {
13260  OtherTrainDataPtr = &TrainDataVector.at(x);
13261  ReverseCount++;
13262  ReverseEntryPtr = &AVEntry;
13263  ReverseTDVectorNumber = x;
13264  }
13265  }
13266  }
13267  }
13268  }
13269 
13270  if(ReverseCount == 0)
13271  {
13272  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
13273  TrainDataVector.clear();
13274  Utilities->CallLogPop(837);
13275  return(false);
13276  }
13277  if(ReverseCount > 1)
13278  {
13279  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13280  OtherHeadCode);
13281  TrainDataVector.clear();
13282  Utilities->CallLogPop(838);
13283  return(false);
13284  }
13285  // these will all be false for !Shuttle
13286  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
13287  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
13288  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
13289  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
13290 
13291  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
13292  {
13293  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
13294  TrainDataVector.clear();
13295  Utilities->CallLogPop(1058);
13296  return(false);
13297  }
13298  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
13299  {
13300  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
13301  TrainDataVector.clear();
13302  Utilities->CallLogPop(1059);
13303  return(false);
13304  }
13305  if(ForwardEntryPtr->LocationName == "")
13306  {
13307  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13308  ". One or other service does not have a location set");
13309  TrainDataVector.clear();
13310  Utilities->CallLogPop(526);
13311  return(false);
13312  }
13313  if(ReverseEntryPtr->LocationName == "")
13314  {
13315  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13316  ". One or other service does not have a location set");
13317  TrainDataVector.clear();
13318  Utilities->CallLogPop(527);
13319  return(false);
13320  }
13321  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13322  {
13323  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13324  " is at a different location to the referencing train " + MainHeadCode);
13325  TrainDataVector.clear();
13326  Utilities->CallLogPop(842);
13327  return(false);
13328  }
13329  // ignore shuttle repeat links for first time check
13330  if(!Shuttle)
13331  {
13332  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13333  {
13334  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13335  " has a different event time to the referencing train " + MainHeadCode);
13336  TrainDataVector.clear();
13337  Utilities->CallLogPop(525);
13338  return(false);
13339  }
13340  }
13341  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
13342  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
13343  if(ForwardShuttleStart && ReverseShuttleFinish)
13344  // Shuttle must be true if these are true
13345  {
13346  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
13347  {
13348  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
13349  " first repeat restart time not consistent with finish service " + OtherHeadCode);
13350  TrainDataVector.clear();
13351  Utilities->CallLogPop(1055);
13352  return(false);
13353  }
13354  }
13355  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
13356  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
13357  {
13358  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13359  {
13360  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
13361  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13362  TrainDataVector.clear();
13363  Utilities->CallLogPop(528);
13364  return(false);
13365  }
13366  }
13367  if(ReverseEntryPtr->Command == "Fjo")
13368  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
13369  {
13370  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13371  {
13372  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
13373  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13374  TrainDataVector.clear();
13375  Utilities->CallLogPop(862);
13376  return(false);
13377  }
13378  }
13379  if(ReverseEntryPtr->Command == "Fns")
13380  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
13381  {
13382  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13383  {
13384  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
13385  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13386  TrainDataVector.clear();
13387  Utilities->CallLogPop(529);
13388  return(false);
13389  }
13390  }
13391  if(ForwardEntryPtr->Command == "Sfs")
13392  {
13393  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
13394  {
13395  SecondPassMessage(GiveMessages,
13396  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
13397  MainHeadCode);
13398  TrainDataVector.clear();
13399  Utilities->CallLogPop(530);
13400  return(false);
13401  }
13402  }
13403  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
13404  {
13405  if(ReverseEntryPtr->Command != "Sfs")
13406  {
13407  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
13408  MainHeadCode);
13409  TrainDataVector.clear();
13410  Utilities->CallLogPop(839);
13411  return(false);
13412  }
13413  else
13414  {
13415  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
13416  {
13417  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
13418  TrainDataVector.clear();
13419  Utilities->CallLogPop(849);
13420  return(false);
13421  }
13422  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
13423  {
13424  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
13425  TrainDataVector.clear();
13426  Utilities->CallLogPop(850);
13427  return(false);
13428  }
13429  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
13430  {
13431  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
13432  TrainDataVector.clear();
13433  Utilities->CallLogPop(846);
13434  return(false);
13435  }
13436  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13437  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13438  if(OtherTrainDataPtr->Description == "")
13439  {
13440  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13441  }
13442  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
13443  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13444  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13445  }
13446  }
13447  if(ForwardEntryPtr->Command == "Sns")
13448  {
13449  if(ReverseEntryPtr->Command != "Fns")
13450  {
13451  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
13452  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
13453  TrainDataVector.clear();
13454  Utilities->CallLogPop(531);
13455  return(false);
13456  }
13457  }
13458  if(ForwardEntryPtr->Command == "Fns")
13459  {
13460  if(ReverseEntryPtr->Command != "Sns")
13461  {
13462  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
13463  " and forms a new service with headcode " + OtherHeadCode);
13464  TrainDataVector.clear();
13465  Utilities->CallLogPop(840);
13466  return(false);
13467  }
13468  else
13469  {
13470  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13471  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13472  if(OtherTrainDataPtr->Description == "")
13473  {
13474  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13475  }
13476  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13477  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13478  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13479  }
13480  }
13481  if(ForwardEntryPtr->Command == "jbo")
13482  {
13483  if(ReverseEntryPtr->Command != "Fjo")
13484  {
13485  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
13486  " and is joined by a train with headcode " + OtherHeadCode);
13487  TrainDataVector.clear();
13488  Utilities->CallLogPop(841);
13489  return(false);
13490  }
13491  }
13492  if(ForwardEntryPtr->Command == "Fjo")
13493  {
13494  if(ReverseEntryPtr->Command != "jbo")
13495  {
13496  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
13497  " and joins a train with headcode " + OtherHeadCode);
13498  TrainDataVector.clear();
13499  Utilities->CallLogPop(532);
13500  return(false);
13501  }
13502  else
13503  {
13504  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13505  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13506  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
13507  {
13508  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13509  }
13510  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
13511  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
13512  }
13513  }
13514  if(ForwardShuttleStart)
13515  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
13516  {
13517  if(!ReverseShuttleFinish)
13518  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
13519  {
13520  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
13521  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
13522  TrainDataVector.clear();
13523  Utilities->CallLogPop(1056);
13524  return(false);
13525  }
13526  }
13527  if(ReverseShuttleStart)
13528  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
13529  {
13530  if(!ForwardShuttleFinish)
13531  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
13532  {
13533  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
13534  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
13535  TrainDataVector.clear();
13536  Utilities->CallLogPop(1057);
13537  return(false);
13538  }
13539  else
13540  {
13541  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13542  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13543 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
13544  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
13545 */
13546  }
13547  }
13548  // check repeat information consistent if present
13549  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
13550  // and those not accessed here
13551 
13552  // still need to check the non-repeating links and that they have no repeats - do that outside this function
13553  bool MainRepeat = false, OtherRepeat = false;
13554  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
13555 
13556  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13557  {
13558  MainRepeat = true;
13559  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
13560  }
13561  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13562  {
13563  OtherRepeat = true;
13564  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
13565  }
13566  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
13567  {
13568  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
13569  " and the associated train with headcode " + OtherHeadCode);
13570  TrainDataVector.clear();
13571  Utilities->CallLogPop(844);
13572  return(false);
13573  }
13574  if(MainRepeat && OtherRepeat)
13575  {
13576  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
13577  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
13578  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
13579  {
13580  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
13581  " and the associated train with headcode " + OtherHeadCode);
13582  TrainDataVector.clear();
13583  Utilities->CallLogPop(845);
13584  return(false);
13585  }
13586  }
13587  Utilities->CallLogPop(863);
13588  return(true);
13589 }
13590 
13591 // ---------------------------------------------------------------------------
13592 
13593 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
13594 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
13595 {
13596  // strip spaces from extreme ends of input
13597  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
13598  if(Input == "")
13599  {
13600  Utilities->CallLogPop(856);
13601  return;
13602  }
13603  while(Input[1] == ' ')
13604  {
13605  if(Input.Length() > 1)
13606  {
13607  Input = Input.SubString(2, Input.Length() - 1);
13608  }
13609  else
13610  {
13611  Input = "";
13612  Utilities->CallLogPop(857);
13613  return;
13614  }
13615  }
13616  if(Input == "")
13617  {
13618  Utilities->CallLogPop(858);
13619  return;
13620  }
13621  while(Input[Input.Length()] == ' ')
13622  {
13623  if(Input.Length() > 1)
13624  {
13625  Input = Input.SubString(1, Input.Length() - 1);
13626  }
13627  else
13628  {
13629  Input = "";
13630  Utilities->CallLogPop(859);
13631  return;
13632  }
13633  }
13634  // now strip spaces immediately after all commas and semicolons within the text
13635  AnsiString Output = "";
13636  bool DelimiterFound = false;
13637 
13638  for(int x = 1; x < Input.Length() + 1; x++)
13639  {
13640  if(DelimiterFound)
13641  {
13642  if(Input[x] == ' ')
13643  {
13644  continue;
13645  }
13646  }
13647  if((Input[x] != ',') && (Input[x] != ';'))
13648  {
13649  DelimiterFound = false;
13650  Output = Output + Input[x];
13651  }
13652  else
13653  {
13654  DelimiterFound = true;
13655  Output = Output + Input[x];
13656  }
13657  }
13658  if(Output == "")
13659  {
13660  Input = "";
13661  Utilities->CallLogPop(860);
13662  return;
13663  }
13664  // now strip spaces immediately before all commas and semicolons within the text
13665  Input = Output;
13666  Output = "";
13667  DelimiterFound = false;
13668  for(int x = Input.Length(); x > 0; x--)
13669  {
13670  if(DelimiterFound)
13671  {
13672  if(Input[x] == ' ')
13673  {
13674  continue;
13675  }
13676  }
13677  if((Input[x] != ',') && (Input[x] != ';'))
13678  {
13679  DelimiterFound = false;
13680  Output = AnsiString(Input[x]) + Output;
13681  }
13682  else
13683  {
13684  DelimiterFound = true;
13685  Output = AnsiString(Input[x]) + Output;
13686  }
13687  }
13688  Input = Output;
13689  Utilities->CallLogPop(861);
13690 }
13691 
13692 // ---------------------------------------------------------------------------
13693 
13694 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
13695 // checks if an Snt or Snt-sh entry followed (somewhere, not necessarily immediately) by a TimeLoc has the same LocationName
13696 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
13697 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
13698 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
13699 // are done in this function, they must be done elsewhere.
13700 {
13701  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
13702  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13703 
13704  LocationName = "";
13705  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
13706  {
13707  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
13708  }
13710  {
13711  Utilities->CallLogPop(852);
13712  return(false);
13713  }
13714  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
13715  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
13716 
13717  if(LocRear != "")
13718  {
13719  LocationName = LocRear;
13720  }
13721  else
13722  {
13723  LocationName = LocFront;
13724  }
13725  if(LocationName == "")
13726  {
13727  Utilities->CallLogPop(1036);
13728  return(false);
13729  }
13730  if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed == 0))
13731  {
13732  Utilities->CallLogPop(1773);
13733  return(true);
13734  }
13735  else if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed > 0))
13736  {
13737  LocationName = "";
13738  Utilities->CallLogPop(1784);
13739  return(false);
13740  }
13741  // here if not a signaller start entry so must be at least one more entry
13742  const TActionVectorEntry &AVEntry1 = TDEntry.ActionVector.at(1);
13743 
13744  // has to be at least 2 AV entries to pass the > 1 comma test in the preliminary check
13745  if(((AVEntry1.Command == "Frh") || (AVEntry1.Command == "Fjo") || (AVEntry1.Command == "F-nshs") || (AVEntry1.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
13746  {
13747  //added F-nshs at v2.5.1 so can stay at a
13748  Utilities->CallLogPop(1037); //location until become a new shuttle service
13749  return(true); //added Fns at same time as saw no reason to exclude
13750  }
13751  AnsiString TimeLocLocationName;
13752  bool FoundFlag = false;
13753 
13754  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
13755  {
13756  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
13757  if(AVEntry.FormatType == TimeLoc)
13758  {
13759  FoundFlag = true;
13760  TimeLocLocationName = AVEntry.LocationName;
13761  break;
13762  }
13763  }
13764  if(!FoundFlag)
13765  {
13766  Utilities->CallLogPop(853);
13767  return(false);
13768  }
13769  if(TimeLocLocationName == LocationName)
13770  {
13771  Utilities->CallLogPop(854);
13772  return(true);
13773  }
13774  Utilities->CallLogPop(855);
13775  return(false);
13776 }
13777 
13778 // ---------------------------------------------------------------------------
13779 
13780 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
13781 {
13782  // checks that the new train start elements are valid - both exist & are connected, and that not
13783  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
13784  // & not starting with front on a continuation
13785  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
13786  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
13787 
13788  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
13789  if(RearPosition < 0)
13790  // error message given in GetTrackVectorPositionFromString
13791  {
13792  Utilities->CallLogPop(759);
13793  return(false);
13794  }
13795  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
13796  if(FrontPosition < 0)
13797  // error message given in GetTrackVectorPositionFromString
13798  {
13799  Utilities->CallLogPop(760);
13800  return(false);
13801  }
13802  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
13803  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
13804  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
13805 
13806  // check front & rear connected
13807  for(int x = 0; x < 4; x++)
13808  {
13809  if(RearTrackElement.Conn[x] == FrontPosition)
13810  {
13811  RearExitPos = x;
13812  break;
13813  }
13814  if(x == 3)
13815  {
13816  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
13817  Utilities->CallLogPop(762);
13818  return(false);
13819  }
13820  }
13821  // check not starting with front on a continuation
13822  if(FrontType == Continuation)
13823  {
13824  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
13825  Utilities->CallLogPop(937);
13826  return(false);
13827  }
13828  // check not starting on a level crossing
13829  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
13830  {
13831  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
13832  Utilities->CallLogPop(1951);
13833  return(false);
13834  }
13835  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
13836  {
13837  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
13838  Utilities->CallLogPop(1952);
13839  return(false);
13840  }
13841  // check if trying to start on diverging leg of points
13842  if((RearType == Points) && (RearExitPos == 3))
13843  {
13844  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
13845  Utilities->CallLogPop(936);
13846  return(false);
13847  }
13848  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
13849  {
13850  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
13851  Utilities->CallLogPop(1808);
13852  return(false);
13853  }
13854  Utilities->CallLogPop(905);
13855  return(true);
13856 }
13857 
13858 // ---------------------------------------------------------------------------
13859 
13860 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
13861 // Rear & front element validity already checked in CheckStartPositionValidity
13862 // This checks for points in correct orientation, no train at start position and not starting on a locked route
13863 {
13864  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
13865  AnsiString(RearExitPos));
13866  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
13867 
13868  if(RearTrackElement.TrackType == Continuation)
13869  {
13870  EventType = FailTrainEntry;
13871  }
13872  else
13873  {
13874  EventType = FailCreateTrain;
13875  }
13876  int FrontPosition = RearTrackElement.Conn[RearExitPos];
13877  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
13878  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
13879  TTrackType RearType = RearTrackElement.TrackType;
13880  TTrackType FrontType = FrontTrackElement.TrackType;
13881  AnsiString RearName, FrontName;
13882 
13883  if(RearTrackElement.ActiveTrackElementName != "")
13884  {
13885  RearName = RearTrackElement.ActiveTrackElementName;
13886  }
13887  else
13888  {
13889  RearName = RearTrackElement.ElementID;
13890  }
13891  if(FrontTrackElement.ActiveTrackElementName != "")
13892  {
13893  FrontName = FrontTrackElement.ActiveTrackElementName;
13894  }
13895  else
13896  {
13897  FrontName = FrontTrackElement.ElementID;
13898  }
13899  TPrefDirElement PrefDirElement; // needed for next function but not used
13900  int LockedVectorNumber; // needed for next function but not used
13901 
13902  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
13903  {
13904  if(ReportFlag)
13905  {
13906  if(EventType == FailCreateTrain)
13907  {
13908  EventType = FailCreateLockedRoute;
13909  }
13910  else
13911  {
13912  EventType = FailEnterLockedRoute;
13913  }
13914  LogActionError(47, HeadCode, "", EventType, FrontName);
13915  }
13916  Utilities->CallLogPop(940);
13917  return(false);
13918  }
13919  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
13920  {
13921  if(ReportFlag)
13922  {
13923  if(EventType == FailCreateTrain)
13924  {
13925  EventType = FailCreateLockedRoute;
13926  }
13927  else
13928  {
13929  EventType = FailEnterLockedRoute;
13930  }
13931  LogActionError(48, HeadCode, "", EventType, RearName);
13932  }
13933  Utilities->CallLogPop(1809);
13934  return(false);
13935  }
13936  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
13937  {
13938  if(ReportFlag)
13939  {
13940  LogActionError(27, HeadCode, "", EventType, RearName);
13941  }
13942  Utilities->CallLogPop(1810);
13943  return(false);
13944  }
13945  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
13946  {
13947  if(ReportFlag)
13948  {
13949  if(EventType == FailCreateTrain)
13950  {
13951  LogActionError(28, HeadCode, "", EventType, FrontName);
13952  }
13953  else
13954  {
13955  LogActionError(43, HeadCode, "", EventType, RearName);
13956  }
13957  }
13958  Utilities->CallLogPop(941);
13959  return(false);
13960  }
13961  if(RearType == Bridge)
13962  {
13963  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13964  {
13965  if(ReportFlag)
13966  {
13967  LogActionError(29, HeadCode, "", EventType, RearName);
13968  }
13969  Utilities->CallLogPop(942);
13970  return(false);
13971  }
13972  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13973  {
13974  if(ReportFlag)
13975  {
13976  LogActionError(30, HeadCode, "", EventType, RearName);
13977  }
13978  Utilities->CallLogPop(943);
13979  return(false);
13980  }
13981  }
13982  if(FrontType == Bridge)
13983  {
13984  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13985  {
13986  if(ReportFlag)
13987  {
13988  if(EventType == FailCreateTrain)
13989  {
13990  LogActionError(31, HeadCode, "", EventType, FrontName);
13991  }
13992  else
13993  {
13994  LogActionError(44, HeadCode, "", EventType, RearName);
13995  }
13996  }
13997  Utilities->CallLogPop(944);
13998  return(false);
13999  }
14000  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14001  {
14002  if(ReportFlag)
14003  {
14004  if(EventType == FailCreateTrain)
14005  {
14006  LogActionError(45, HeadCode, "", EventType, FrontName);
14007  }
14008  else
14009  {
14010  LogActionError(46, HeadCode, "", EventType, RearName);
14011  }
14012  }
14013  Utilities->CallLogPop(945);
14014  return(false);
14015  }
14016  }
14017  EventType = FailCreatePoints;
14018  if(RearType == Points)
14019  {
14020  if(RearTrackElement.Attribute == 1)
14021  {
14022  if(ReportFlag)
14023  {
14024  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
14025  }
14026  Utilities->CallLogPop(933);
14027  return(false);
14028  }
14029  }
14030  if(FrontType == Points)
14031  {
14032  if(FrontTrackElement.Attribute == 1)
14033  {
14034  if(ReportFlag)
14035  {
14036  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
14037  }
14038  Utilities->CallLogPop(934);
14039  return(false);
14040  }
14041  }
14042 
14043  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
14044  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
14045  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
14046  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
14047  int RouteNumber; //not used
14048  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
14049  {
14050  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
14051  {
14052  EventType = FailEntryRouteSetAgainst;
14053  if(ReportFlag)
14054  {
14055  LogActionError(63, HeadCode, "", EventType, RearName);
14056  }
14057  Utilities->CallLogPop(2317);
14058  return(false);
14059  }
14060  }
14061  Utilities->CallLogPop(939);
14062  return(true);
14063 }
14064 
14065 // ---------------------------------------------------------------------------
14066 
14067 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
14068 {
14069  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
14070  "," + AnsiString(IncDigits));
14071  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
14072  {
14073  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
14074  }
14075  if(!Last2CharactersBothDigits(2, BaseHeadCode))
14076  {
14077  Utilities->CallLogPop(1893);
14078  return(BaseHeadCode);
14079  }
14080  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
14081  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
14082 
14083  while(NextRepeatDigits >= 100)
14084  {
14085  NextRepeatDigits -= 100; // rolls over after 99
14086  }
14087  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
14088 
14089  if(NextRepeatDigitsStr.Length() < 2)
14090  {
14091  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
14092  }
14093  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
14094 
14095  Utilities->CallLogPop(1365);
14096  return(NextRepeatHeadCode);
14097 }
14098 
14099 // ---------------------------------------------------------------------------
14100 
14101 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
14102 {
14103  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
14104  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
14105  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
14106 
14107  Utilities->CallLogPop(1366);
14108  return(NextRepeatTime);
14109 }
14110 
14111 // ---------------------------------------------------------------------------
14112 
14113 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
14114 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
14115 {
14116  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
14117  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
14118  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14119  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14120  int RepeatSecs = RepeatMinutes * 60;
14121 
14122  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
14123  {
14124  Utilities->CallLogPop(1367);
14125  return(false);
14126  }
14127  else
14128  {
14129  Utilities->CallLogPop(1368);
14130  return(true);
14131  }
14132 }
14133 
14134 // ---------------------------------------------------------------------------
14135 
14136 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
14137 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14138 {
14139  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
14140  NonRepeatingHeadCode);
14141  int ForwardCount = 0;
14142  int ReverseCount = 0;
14143  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14144  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14145  // Forward corresponds to Main, Reverse to Other
14146  TTrainDataEntry *MainTrainDataPtr = 0;
14147  TTrainDataEntry *OtherTrainDataPtr = 0;
14148 
14149  // forward check
14150  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14151  {
14152  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14153  if(TDEntry.HeadCode == MainHeadCode)
14154  {
14155  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14156  {
14157  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14158  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
14159  {
14160  MainTrainDataPtr = &TrainDataVector.at(x);
14161  ForwardEntryPtr = &AVEntry;
14162  ForwardCount++;
14163  ForwardTDVectorNumber = x;
14164  }
14165  }
14166  }
14167  }
14168  if(ForwardCount == 0)
14169  // this is an exception because the headcodes are selected in the same order as the forward check
14170  {
14171  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
14172  }
14173  if(ForwardCount > 1)
14174  {
14175  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
14176  MainHeadCode);
14177  TrainDataVector.clear();
14178  Utilities->CallLogPop(1061);
14179  return(false);
14180  }
14181  // reverse check
14182  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14183  {
14184  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14185  if(TDEntry.HeadCode == NonRepeatingHeadCode)
14186  {
14187  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14188  {
14189  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14190  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14191  {
14192  OtherTrainDataPtr = &TrainDataVector.at(x);
14193  ReverseCount++;
14194  ReverseEntryPtr = &AVEntry;
14195  ReverseTDVectorNumber = x;
14196  }
14197  }
14198  }
14199  }
14200 
14201  if(ReverseCount == 0)
14202  {
14203  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
14204  TrainDataVector.clear();
14205  Utilities->CallLogPop(1062);
14206  return(false);
14207  }
14208  if(ReverseCount > 1)
14209  {
14210  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14211  NonRepeatingHeadCode);
14212  TrainDataVector.clear();
14213  Utilities->CallLogPop(1063);
14214  return(false);
14215  }
14216  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
14217  {
14218  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
14219  TrainDataVector.clear();
14220  Utilities->CallLogPop(1064);
14221  return(false);
14222  }
14223  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
14224  {
14225  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
14226  TrainDataVector.clear();
14227  Utilities->CallLogPop(1065);
14228  return(false);
14229  }
14230  if(ForwardEntryPtr->LocationName == "")
14231  {
14232  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14233  ". One or other service does not have a location set");
14234  TrainDataVector.clear();
14235  Utilities->CallLogPop(1066);
14236  return(false);
14237  }
14238  if(ReverseEntryPtr->LocationName == "")
14239  {
14240  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14241  ". One or other service does not have a location set");
14242  TrainDataVector.clear();
14243  Utilities->CallLogPop(1067);
14244  return(false);
14245  }
14246  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14247  {
14248  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
14249  " is at a different location to the referencing train " + MainHeadCode);
14250  TrainDataVector.clear();
14251  Utilities->CallLogPop(1068);
14252  return(false);
14253  }
14254  if(ForwardEntryPtr->Command == "F-nshs")
14255  // i.e. the non repeating link into the shuttle service
14256  {
14257  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14258  {
14259  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
14260  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
14261  TrainDataVector.clear();
14262  Utilities->CallLogPop(1069);
14263  return(false);
14264  }
14265  }
14266  if(ForwardEntryPtr->Command == "Fns-sh")
14267  // i.e. the non repeating link out from the shuttle service
14268  {
14269  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
14270  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
14271  {
14272  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
14273  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
14274  TrainDataVector.clear();
14275  Utilities->CallLogPop(1070);
14276  return(false);
14277  }
14278  }
14279  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
14280  // i.e. a non repeating link to or from the shuttle service
14281  {
14282  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14283  {
14284  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
14285  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
14286  TrainDataVector.clear();
14287  Utilities->CallLogPop(1071);
14288  return(false);
14289  }
14290  }
14291 /* it's allowed to have a different description
14292  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
14293  {
14294  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
14295  {
14296  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
14297  TrainDataVector.clear();
14298  Utilities->CallLogPop(1072);
14299  return false;
14300  }
14301  }
14302 */
14303  if(ForwardEntryPtr->Command == "Sns-sh")
14304  {
14305  if(ReverseEntryPtr->Command != "F-nshs")
14306  {
14307  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
14308  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
14309  TrainDataVector.clear();
14310  Utilities->CallLogPop(1073);
14311  return(false);
14312  }
14313  }
14314  if(ForwardEntryPtr->Command == "F-nshs")
14315  {
14316  if(ReverseEntryPtr->Command != "Sns-sh")
14317  {
14318  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
14319  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
14320  TrainDataVector.clear();
14321  Utilities->CallLogPop(1074);
14322  return(false);
14323  }
14324  else
14325  {
14326  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14327  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
14328  if(OtherTrainDataPtr->Description == "")
14329  {
14330  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14331  }
14332  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14333  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14334  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14335  }
14336  }
14337  if(ForwardEntryPtr->Command == "Sns-fsh")
14338  {
14339  if(ReverseEntryPtr->Command != "Fns-sh")
14340  {
14341  SecondPassMessage(GiveMessages,
14342  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
14343  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
14344  TrainDataVector.clear();
14345  Utilities->CallLogPop(1075);
14346  return(false);
14347  }
14348  }
14349  if(ForwardEntryPtr->Command == "Fns-sh")
14350  {
14351  if(ReverseEntryPtr->Command != "Sns-fsh")
14352  {
14353  SecondPassMessage(GiveMessages,
14354  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
14355  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
14356  TrainDataVector.clear();
14357  Utilities->CallLogPop(1076);
14358  return(false);
14359  }
14360  else
14361  {
14362  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
14363  // links to the non-repeating non-shuttle linked service
14364  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14365  // needed for creating formatted timetable
14366  if(OtherTrainDataPtr->Description == "")
14367  {
14368  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14369  }
14370  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14371  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14372  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14373  }
14374  }
14375  Utilities->CallLogPop(1077);
14376  return(true);
14377 }
14378 
14379 // ---------------------------------------------------------------------------
14380 
14381 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
14382 // Forward train is the finish shuttle entry 'Fns-sh'.
14383 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
14384 {
14385  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
14386  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
14387  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14388  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14389  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
14390 
14391  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
14392  {
14393  Utilities->CallLogPop(1369);
14394  return(false);
14395  }
14396  else
14397  {
14398  Utilities->CallLogPop(1370);
14399  return(true);
14400  }
14401 }
14402 
14403 // ---------------------------------------------------------------------------
14404 
14405 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
14406 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14407 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14408 // don't ever need to and as designed would skip repeats.
14409 
14410 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
14411 {
14412  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
14413  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
14414  {
14415  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
14416  }
14417  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
14418  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
14419  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14420 
14421  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14422  {
14423  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14424  TrainDataVector.clear();
14425  Utilities->CallLogPop(1091);
14426  return(false);
14427  }
14428  while(LastActionCommand == "Fns")
14429  {
14430  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
14431  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14432  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14433  {
14434  SecondPassMessage(GiveMessages,
14435  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
14436  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14437  TrainDataVector.clear();
14438  Utilities->CallLogPop(1092);
14439  return(false);
14440  }
14441  }
14442  // exit the 'while' with LastActionCommand FSH-XX
14443  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
14444  {
14445  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
14446  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
14447  ". The linking of two or more shuttles is not permitted.");
14448  TrainDataVector.clear();
14449  Utilities->CallLogPop(1093);
14450  return(false);
14451  }
14452  Utilities->CallLogPop(1094);
14453  return(true);
14454 }
14455 
14456 // ---------------------------------------------------------------------------
14457 
14458 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
14459 {
14460  if(!GiveMessages)
14461  {
14462  return;
14463  }
14464  // if(ServiceReference == "") ShowMessage(Message);
14465  if(!CheckHeadCodeValidity(12, false, ServiceReference))
14466  {
14467  ShowMessage(Message);
14468  }
14469  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
14470  // false means don't give messages within the function
14471  else
14472  {
14473  ShowMessage("Service " + ServiceReference + ": " + Message);
14474  }
14475 }
14476 
14477 // ---------------------------------------------------------------------------
14478 
14479 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
14480 {
14481  if(!GiveMessages)
14482  {
14483  return;
14484  }
14485  ShowMessage(Message);
14486 }
14487 
14488 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
14489 // ---------------------------------------------------------------------------
14490 
14491 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
14492 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
14493 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
14494 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
14495 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
14496 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
14497 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
14498 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
14499 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
14500 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
14501 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
14502 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
14503 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
14504 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
14505 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
14506 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
14507 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
14508 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
14509 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
14510 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
14511 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
14512 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
14513 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
14514 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
14515 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
14516 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
14517 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
14518 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
14519 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
14520 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
14521 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5
14522 {
14523  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
14524  AnsiString(ActionEventType) + "," + LocationID);
14525  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
14526 
14527  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
14528  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + " " + HeadCode; //added at v2.9.1 to give more info to user
14529 
14530  Prefix = " ERROR: ";
14531  if(ActionEventType == FailTrainEntry)
14532  {
14533  Prefix = " HELD: ";
14534  ErrorLog = " can't enter railway, train obstructing entry position ";
14535  WarningStr = " can't enter railway, train obstructing entry position ";
14536  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
14537  }
14538  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
14539  {
14540  Prefix = " HELD: ";
14541  ErrorLog = " can't enter railway, route set against it at entry position ";
14542  WarningStr = " can't enter railway, route set against it at entry position ";
14543  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
14544  }
14545  else if(ActionEventType == FailCreateTrain)
14546  {
14547  Prefix = " HELD: ";
14548  ErrorLog = " can't be created, train obstructing ";
14549  WarningStr = " can't be created, train obstructing ";
14550  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
14551  }
14552  else if(ActionEventType == FailCreateLockedRoute)
14553  {
14554  Prefix = " HELD: ";
14555  ErrorLog = " can't be created on a locked route at ";
14556  WarningStr = " can't be created on a locked route at ";
14557  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
14558  }
14559  else if(ActionEventType == FailEnterLockedRoute)
14560  {
14561  Prefix = " HELD: ";
14562  ErrorLog = " can't enter on a locked route at ";
14563  WarningStr = " can't enter on a locked route at ";
14564  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
14565  }
14566  else if(ActionEventType == FailCreatePoints)
14567  {
14568  Prefix = " HELD: ";
14569  ErrorLog = " can't be created, diverging points at ";
14570  WarningStr = " can't be created, diverging points at ";
14571  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
14572  }
14573  else if(ActionEventType == FailUnexpectedExitRailway)
14574  {
14575  ErrorLog = " left railway unexpectedly at ";
14576  UnexpectedExits++;
14577  }
14578  else if(ActionEventType == FailIncorrectExit)
14579  {
14580  ErrorLog = " left railway at an incorrect exit at ";
14581  IncorrectExits++;
14582  }
14583  else if(ActionEventType == FailLocTooShort)
14584  {
14585  ErrorLog = " failed to split - location too short at ";
14586  WarningStr = " failed to split, location too short at ";
14587  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
14588  }
14589  else if(ActionEventType == FailSplitDueToOtherTrain)
14590  {
14591  Prefix = " HELD: ";
14592  ErrorLog = " unable to split - other train obstructing at ";
14593  WarningStr = " unable to split - other train obstructing at ";
14594  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
14595  }
14596  else if(ActionEventType == FailUnexpectedBuffers)
14597  {
14598  ErrorLog = " stopped at buffers unexpectedly at position ";
14599  }
14600  else if(ActionEventType == FailMissedArrival)
14601  {
14602  ErrorLog = " failed to stop at ";
14603  MissedStops++;
14604  }
14605  else if(ActionEventType == FailMissedSplit)
14606  {
14607  ErrorLog = " failed to split at ";
14609  }
14610  else if(ActionEventType == FailMissedJBO)
14611  {
14612  ErrorLog = " failed to be joined by other train at ";
14614  }
14615  else if(ActionEventType == FailMissedJoinOther)
14616  {
14617  ErrorLog = " failed to join other train at ";
14619  }
14620  else if(ActionEventType == FailMissedTerminate)
14621  {
14622  ErrorLog = " failed to terminate at ";
14624  }
14625  else if(ActionEventType == FailMissedNewService)
14626  {
14627  ErrorLog = " failed to form new service at ";
14629  }
14630  else if(ActionEventType == FailMissedExitRailway)
14631  {
14632  ErrorLog = " failed to exit railway ";
14634  }
14635  else if(ActionEventType == FailMissedChangeDirection)
14636  {
14637  ErrorLog = " failed to change direction at ";
14639  }
14640  else if(ActionEventType == FailMissedPass)
14641  {
14642  ErrorLog = " failed to pass ";
14644  }
14645  else if(ActionEventType == FailBuffersPreventingStart)
14646  {
14647  ErrorLog = " facing buffers and unable to start at ";
14648  }
14649  else if(ActionEventType == FailDerailed)
14650  {
14651  ErrorLog = " DERAILED at position ";
14652  Prefix = " DERAILMENT: ";
14653  Derailments++;
14654  }
14655  else if(ActionEventType == FailBufferCrash)
14656  {
14657  ErrorLog = " CRASHED INTO BUFFERS at ";
14658  Prefix = " CRASH: ";
14659  CrashedTrains++;
14660  }
14661  else if(ActionEventType == FailLevelCrossingCrash)
14662  {
14663  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
14664  Prefix = " CRASH: ";
14665  CrashedTrains++;
14666  }
14667  else if(ActionEventType == FailCrashed)
14668  {
14669  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
14670  Prefix = " CRASH: ";
14671  CrashedTrains++;
14672  CrashedTrains++;
14673  }
14674  else if(ActionEventType == FailSPAD)
14675  {
14676  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
14677  Prefix = " SPAD: ";
14678  SPADEvents++;
14679  }
14680  else if(ActionEventType == FailLockedRoute)
14681  {
14682  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
14683  Prefix = " SPAD RISK: ";
14684  SPADRisks++;
14685  }
14686  else if(ActionEventType == RouteForceCancelled)
14687  {
14688  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
14689  }
14690  else if(ActionEventType == WaitingForJBO)
14691  {
14692  Prefix = " WARNING: ";
14693  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
14694  WarningStr = " waiting to join " + OtherHeadCode + " at ";
14695  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
14696  }
14697  else if(ActionEventType == WaitingForFJO)
14698  {
14699  Prefix = " WARNING: ";
14700  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
14701  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
14702  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
14703  }
14704 
14705  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
14706  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
14707  Utilities->CallLogPop(1371);
14708 }
14709 
14710 // ---------------------------------------------------------------------------
14711 
14713 {
14714 /*
14715  TrainDataEntry
14716  AnsiString HeadCode, Description;//null on creation
14717  int StartSpeed, MaxRunningSpeed;//both kph
14718  int RepeatNumber;
14719  TActionVector ActionVector;
14720  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
14721  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
14722 
14723  ActionVectorEntry
14724  TTimetableEntryType FormatType;
14725  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
14726  AnsiString LocationName, Command, OtherHeadCode;//null on creation
14727  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
14728  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
14729  int RepeatNumber;
14730 
14731  TrainOperatingData
14732  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
14733  int TrainID;
14734  TRunningEntry RunningEntry;
14735  TDateTime StartTime;
14736  AnsiString HeadCode;
14737 */
14738  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
14739  std::ofstream OutFile("TrainData.csv");
14740 
14741  if(OutFile == 0)
14742  {
14743  ShowMessage("Output file TrainData.csv failed to open");
14744  Utilities->CallLogPop(1372);
14745  return;
14746  }
14747  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14748  {
14749  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14750  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
14751 
14752  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
14753  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
14754 
14755  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
14756  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
14757  "RepeatNumber" << '\n' << '\n';
14758  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14759  {
14760  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14761  AnsiString TimetableEntryTypeStr;
14762  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
14763  switch(AVEntry.FormatType)
14764  {
14765  case 0:
14766  {
14767  TimetableEntryTypeStr = "NoFormat";
14768  break;
14769  }
14770 
14771  case 1:
14772  {
14773  TimetableEntryTypeStr = "TimeLoc";
14774  break;
14775  }
14776 
14777  case 2:
14778  {
14779  TimetableEntryTypeStr = "TimeTimeLoc";
14780  break;
14781  }
14782 
14783  case 3:
14784  {
14785  TimetableEntryTypeStr = "TimeCmd";
14786  break;
14787  }
14788 
14789  case 4:
14790  {
14791  TimetableEntryTypeStr = "StartNew";
14792  break;
14793  }
14794 
14795  case 5:
14796  {
14797  TimetableEntryTypeStr = "TimeCmdHeadCode";
14798  break;
14799  }
14800 
14801  case 6:
14802  {
14803  TimetableEntryTypeStr = "FinRemHere";
14804  break;
14805  }
14806 
14807  case 7:
14808  {
14809  TimetableEntryTypeStr = "FNSShuttle";
14810  break;
14811  }
14812 
14813  case 8:
14814  {
14815  TimetableEntryTypeStr = "SNTShuttle";
14816  break;
14817  }
14818 
14819  case 9:
14820  {
14821  TimetableEntryTypeStr = "SNSShuttle";
14822  break;
14823  }
14824 
14825  case 10:
14826  {
14827  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
14828  break;
14829  }
14830 
14831  case 11:
14832  {
14833  TimetableEntryTypeStr = "FSHNewService";
14834  break;
14835  }
14836 
14837  case 12:
14838  {
14839  TimetableEntryTypeStr = "Repeat";
14840  break;
14841  }
14842 
14843  default:
14844  {
14845  TimetableEntryTypeStr = "Default";
14846  break;
14847  }
14848  }
14849  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
14850  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
14851  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
14852  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
14853  AVEntry.NumberOfRepeats << '\n';
14854  }
14855  OutFile << '\n';
14856  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
14857  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
14858  {
14859  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
14860  AnsiString RunningEntryStr;
14861  // NotStarted, Running, Exited
14862  switch(TOD.RunningEntry)
14863  {
14864  case 0:
14865  {
14866  RunningEntryStr = "NotStarted";
14867  break;
14868  }
14869 
14870  case 1:
14871  {
14872  RunningEntryStr = "Running";
14873  break;
14874  }
14875 
14876  case 2:
14877  {
14878  RunningEntryStr = "Exited";
14879  break;
14880  }
14881  }
14882  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
14883  }
14884  OutFile << '\n';
14885  }
14886  OutFile.close();
14887  Utilities->CallLogPop(1373);
14888 }
14889 
14890 // ---------------------------------------------------------------------------
14891 
14892 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
14893 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
14894 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
14895 {
14896  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
14897  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14899  ShowMessage(Message);
14900  BaseTime = TDateTime::CurrentDateTime();
14901  StopTTClockFlag = false;
14902  Utilities->CallLogPop(1374);
14903 }
14904 
14905 // ---------------------------------------------------------------------------
14906 
14907 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
14908 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
14909 // from the start of the relevant vectors. Can't save the pointer values
14910 // as these will be different each time the vectors are created
14911 {
14912  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
14913  Utilities->SaveFileInt(SessionFile, TrainVector.size());
14914  for(unsigned int x = 0; x < TrainVector.size(); x++)
14915  {
14916  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
14917  }
14918  Utilities->CallLogPop(1375);
14919 }
14920 
14921 // ---------------------------------------------------------------------------
14922 
14923 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
14924 {
14925  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
14926  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
14927  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14928  // by zero error in calculating AValue, use 1
14929  for(int x = 0; x < NumberOfTrains; x++)
14930  {
14931  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14932  // by zero error in calculating AValue, use 1
14933  NewTrain->LoadOneSessionTrain(0, SessionFile);
14934  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
14935  // added at v2.4.0. have to include as that value not stored in session file
14936  {
14937  NewTrain->StoppedWithoutPower = true;
14938  }
14939  TrainVector.push_back(*NewTrain);
14940  LastTrainLoaded = x;
14941  }
14942  delete NewTrain;
14943  Utilities->CallLogPop(1376);
14944 }
14945 
14946 // ---------------------------------------------------------------------------
14947 
14948 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
14949 {
14950  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
14951  int NumberOfTrains;
14952 
14953  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
14954  {
14955  Utilities->CallLogPop(1377);
14956  return(false);
14957  }
14958  for(int x = 0; x < NumberOfTrains; x++)
14959  {
14960  if(!(TTrain::CheckOneSessionTrain(InFile)))
14961  {
14962  Utilities->CallLogPop(1378);
14963  return(false);
14964  }
14965  }
14966  Utilities->CallLogPop(1379);
14967  return(true);
14968 }
14969 
14970 // ---------------------------------------------------------------------------
14971 
14972 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
14973 {
14974  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
14975  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
14976  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
14977  {
14978  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
14979  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
14980  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
14981  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
14982  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
14983  }
14984  Utilities->CallLogPop(1380);
14985 }
14986 
14987 // ---------------------------------------------------------------------------
14988 
14989 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14990 {
14991  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
14992  TAllRoutes::TLockedRouteClass LockedRouteObject;
14993  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
14994 
14995  for(int x = 0; x < LockedRouteVectorSize; x++)
14996  {
14997  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
14998  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14999  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15000  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
15001  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
15002  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
15003  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
15004  }
15005  Utilities->CallLogPop(1381);
15006 }
15007 
15008 // ---------------------------------------------------------------------------
15009 
15010 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15011 {
15012  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
15013  int LockedRouteVectorSize;
15014 
15015  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
15016  {
15017  Utilities->CallLogPop(1382);
15018  return(false);
15019  }
15020  for(int x = 0; x < LockedRouteVectorSize; x++)
15021  {
15022  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15023  {
15024  Utilities->CallLogPop(1383);
15025  return(false);
15026  }
15027  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15028  {
15029  Utilities->CallLogPop(1384);
15030  return(false);
15031  }
15032  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15033  {
15034  Utilities->CallLogPop(1385);
15035  return(false);
15036  }
15037  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15038  {
15039  Utilities->CallLogPop(1386);
15040  return(false);
15041  }
15042  if(!Utilities->CheckFileDouble(SessionFile))
15043  {
15044  Utilities->CallLogPop(1387);
15045  return(false);
15046  }
15047  }
15048  Utilities->CallLogPop(1388);
15049  return(true);
15050 }
15051 
15052 // ---------------------------------------------------------------------------
15053 
15054 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
15055 {
15056  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
15057  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
15058  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
15059  {
15060  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
15061  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
15062  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
15063  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
15064  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
15065  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
15066  }
15067  Utilities->CallLogPop(1389);
15068 }
15069 
15070 // ---------------------------------------------------------------------------
15071 
15072 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15073 {
15074  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
15075  TContinuationAutoSigEntry ContinuationAutoSigObject;
15076  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
15077 
15078  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15079  {
15080  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15081  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
15082  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
15083  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
15084  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
15085  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
15086  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
15087  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
15088  }
15089  Utilities->CallLogPop(1390);
15090 }
15091 
15092 // ---------------------------------------------------------------------------
15093 
15094 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15095 {
15096  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
15097  int ContinuationAutoSigVectorSize;
15098 
15099  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
15100  {
15101  Utilities->CallLogPop(1391);
15102  return(false);
15103  }
15104  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15105  {
15106  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15107  {
15108  Utilities->CallLogPop(1392);
15109  return(false);
15110  }
15111  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15112  {
15113  Utilities->CallLogPop(1393);
15114  return(false);
15115  }
15116  if(!Utilities->CheckFileDouble(SessionFile))
15117  {
15118  Utilities->CallLogPop(1405);
15119  return(false);
15120  }
15121  if(!Utilities->CheckFileDouble(SessionFile))
15122  {
15123  Utilities->CallLogPop(1406);
15124  return(false);
15125  }
15126  if(!Utilities->CheckFileDouble(SessionFile))
15127  {
15128  Utilities->CallLogPop(1407);
15129  return(false);
15130  }
15131  if(!Utilities->CheckFileDouble(SessionFile))
15132  {
15133  Utilities->CallLogPop(1394);
15134  return(false);
15135  }
15136  }
15137  Utilities->CallLogPop(1395);
15138  return(true);
15139 }
15140 
15141 // ---------------------------------------------------------------------------
15142 
15143 /*
15144  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
15145  {
15146  public:
15147  AnsiString Description; ///< service description
15148  AnsiString HeadCode; ///< service headcode
15149  int RepeatNumber; ///< service RepeatNumber
15150  int IncrementalMinutes; ///< Repeat separation in minutes
15151  int IncrementalDigits; ///< Repeat headcode separation
15152  int VectorPosition; ///< TrackVectorPosition for the continuation element
15153  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
15154  };
15155 
15156 
15157  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
15158  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
15159 */
15160 
15162 // build this into timetable load so session loading can use it too
15163 // being a multimap it automatically sorts in ascending EventTime order
15164 {
15165  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
15167  // need to clear as this called twice when load a session
15168  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15169  {
15170  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
15171  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
15172  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
15173 
15174  if(AVFirstEntry.Command == "Snt")
15175  // new train (no need to include Snt-sh since they can't start at a continuation)
15176  {
15179  {
15181  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
15182  // retains this value for all repeats
15183  CTEEntry.RepeatNumber = 0; // for first entry
15184  CTEEntry.TrainDataEntryPtr = &TDEntry;
15185  // retains this value for all repeats
15186  CTEEntry.HeadCode = TDEntry.HeadCode;
15187  CTEEntry.Description = TDEntry.Description;
15188  CTEEntry.IncrementalMinutes = 0;
15189  CTEEntry.IncrementalDigits = 0;
15190  if(AVLastEntry.FormatType == Repeat)
15191  {
15192  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
15193  // retains this value or 0 for all repeats
15194  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
15195  // retains this value or 0 for all repeats
15196  }
15197  CTEMMP.first = AVFirstEntry.EventTime;
15198  CTEMMP.second = CTEEntry;
15199  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15200  // base entry
15201  if(TDEntry.NumberOfTrains > 1)
15202  {
15203  if(AVLastEntry.FormatType != Repeat)
15204  {
15205  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
15206  }
15207  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
15208  {
15209  CTEEntry.RepeatNumber = y;
15210  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
15211  // CTEEntry.VectorPosition stays same
15212  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
15213  CTEMMP.second = CTEEntry;
15214  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15215  }
15216  }
15217  }
15218  }
15219  }
15220  Utilities->CallLogPop(1396);
15221 }
15222 
15223 // ---------------------------------------------------------------------------
15224 
15226 {
15227  // called when WarningFlashCount == 0 or when press zoomout button
15228  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
15229  if(!Display->ZoomOutFlag)
15230  {
15231  Utilities->CallLogPop(1156);
15232  return;
15233  }
15234  for(unsigned int x = 0; x < TrainVector.size(); x++)
15235  {
15236  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
15237  // if OldPlotElement[x] == -1 then ignore (not plotted)
15239  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
15240  }
15241  Display->Update();
15242  // need to keep this since Update() not called for PlotSmallOutput as too slow
15243  Utilities->CallLogPop(742);
15244 }
15245 
15246 // ---------------------------------------------------------------------------
15247 
15248 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
15249 {
15250  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
15251  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
15252  {
15253  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
15254  }
15255  Utilities->CallLogPop(740);
15256  return(TrainVector.at(VecPos));
15257 }
15258 
15259 // ---------------------------------------------------------------------------
15260 
15261 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
15262 {
15263  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
15264  AnsiString RetStr = "", PartStr = "";
15265 
15266 
15267 /*
15268  Have description & mass etc for train at top - header, then array of actions
15269 
15270  class TActionVectorEntry
15271  {
15272  public:
15273  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
15275  bool SignallerControl;
15277  bool Warning;
15279  int NumberOfRepeats;
15281  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15283  TDateTime EventTime, ArrivalTime, DepartureTime;
15285  TExitList ExitList;
15287  TTimetableFormatType FormatType;
15289  TTimetableLocationType LocationType;
15291  TTimetableSequenceType SequenceType;
15293  TTimetableShuttleLinkType ShuttleLinkType;
15295  TTrainDataEntry *LinkedTrainEntryPtr;
15297  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
15299 
15300  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
15301 
15302  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
15303 
15304  class TTrainOperatingData
15305  {
15306  public:
15307  int TrainID;
15308  TActionEventType EventReported;
15309  TRunningEntry RunningEntry;
15310 
15311  //inline function
15312  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
15313  };
15314 
15315  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
15316 
15317  class TTrainDataEntry
15318  {
15319  public:
15320  AnsiString HeadCode, ServiceReference, Description;
15322  double MaxBrakeRate;
15324  double MaxRunningSpeed;
15326  double PowerAtRail;
15328  int Mass;
15330  int NumberOfTrains;
15332  int SignallerSpeed;
15334  int StartSpeed;
15336  TActionVector ActionVector;
15338  TTrainOperatingDataVector TrainOperatingDataVector;
15340 
15341  //inline function
15342  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
15343  };
15344 
15345  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
15346 
15347  //formatted timetable types
15348  class TOneTrainFormattedEntry
15349  {
15350  AnsiString Action;//includes location if relevanr
15351  AnsiString Time;
15352  };
15353 
15354  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15355 
15356  class TOneCompleteFormattedTrain//headcode + list of actions
15357  {
15358  public:
15359  AnsiString HeadCode;
15360  TOneFormattedTrainVector OneFormattedTrainVector;
15361  };
15362 
15363  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15364 
15365  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15366  {
15367  public:
15368  AnsiString Header;//description, mass, power, brake rate etc
15369  int NumberOfTrains;// number of repeats + 1
15370  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15371  };
15372 
15373 
15374  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15375  //end of formatted timetable types
15376 
15377 */
15378 
15379  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15380 
15381  // format "16/06/2009 20:55:17"
15382  // avoid characters in filename:= / \ : * ? " < > |
15383  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15384 
15385  AnsiString ShortTTName = "";
15386 
15387  for(int x = TTFileName.Length(); x > 0; x--)
15388  {
15389  if(TTFileName[x] == '\\')
15390  {
15391  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
15392  break;
15393  }
15394  }
15395 
15396  ShowMessage("Creates two timetables named " + ShortTTName +
15397  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
15398 
15399  Screen->Cursor = TCursor(-11); // Hourglass
15400 
15401  AnsiString FormatNoDPStr = "#######0";
15402  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
15403 
15405  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
15406  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
15407 
15408  // all timetable in formatted form
15409  //create the AllTTTrains vector
15410  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15411  {
15412  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
15413  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
15414  if(TrainDataEntry.Mass > 0)
15415  {
15416  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
15417  }
15418  if(TrainDataEntry.PowerAtRail > 0)
15419  {
15420  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
15421  }
15422  if(TrainDataEntry.MaxBrakeRate > 0)
15423  {
15424  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
15425  }
15426  if(TrainDataEntry.MaxRunningSpeed > 0)
15427  {
15428  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
15429  }
15430  FirstHeadCode = TrainDataEntry.HeadCode;
15431  int IncDigits = 0, IncMinutes = 0;
15432  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15433  if(!ActionVector.empty())
15434  {
15435  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15436  {
15437  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
15438  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15439  }
15440  }
15441  TTrainFormattedInformation OneTTLine;
15442  // contains all information for a single TT entry (including repeats)
15443  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
15444  {
15445  OneTTLine.Header = "";
15446  if((TrainDataEntry.Description != "") && (MassStr != ""))
15447  {
15448  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
15449  }
15450  else if(TrainDataEntry.Description != "")
15451  {
15452  OneTTLine.Header = TrainDataEntry.Description;
15453  }
15454  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
15455  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
15456  for(unsigned int z = 0; z < ActionVector.size(); z++)
15457  {
15458  TOneTrainFormattedEntry OneTTEntry;
15459  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
15460  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
15461  AnsiString PartStr = "", TimeStr = "";
15462 /*
15463  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
15464  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
15465  ExitRailway};
15466  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
15467  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
15468  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
15469 */
15470  if(ActionVectorEntry.SequenceType == Start)
15471  {
15472  if(ActionVectorEntry.FormatType == StartNew)
15473  {
15474  if(ActionVectorEntry.LocationName != "")
15475  {
15476  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15477  {
15478  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15479  }
15480  else
15481  {
15482  PartStr = "Created at " + ActionVectorEntry.LocationName;
15483  }
15484  }
15485  else // may be a named continuation or other element, and if so report that
15486  {
15487  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
15488  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15489  {
15490  if(LocName != "")
15491  {
15492  PartStr = "Enters at " + LocName;
15493  }
15494  else // use rear position if it's a continuation
15495  {
15496  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15497  }
15498  }
15499  else // not a continuation
15500  {
15501  if(LocName != "")
15502  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
15503  // but include anyway
15504  {
15505  PartStr = "Created at " + LocName;
15506  }
15507  else // use rear position again
15508  {
15509  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15510  }
15511  }
15512  }
15513  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
15514  }
15515  else if(ActionVectorEntry.FormatType == SNTShuttle)
15516  {
15517  if(y == 0) // first train
15518  {
15519  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15520  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
15521  }
15522  else
15523  {
15524  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15525  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15526  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
15527  } // y-1 for headcode above since it is the last repeat value that the train is from
15528 
15529  }
15530  else if(ActionVectorEntry.Command == "Sfs")
15531  {
15532  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
15533  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15534  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
15535  }
15536  else if(ActionVectorEntry.Command == "Sns")
15537  {
15538  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15539  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15540  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
15541  }
15542  else if(ActionVectorEntry.FormatType == SNSShuttle)
15543  {
15544  if(y == 0) // first entry from shuttle
15545  {
15546  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15547  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
15548  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
15549  }
15550  else
15551  {
15552  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15553  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15554  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
15555  } // y-1 for headcode above since it is the last repeat value that the train is from
15556 
15557  }
15558  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
15559  {
15560  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15561  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
15562  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
15563  AnsiString FirstHeadCode = TDE->HeadCode;
15564  int LastRepeatNumber = TDE->NumberOfTrains - 1;
15565  // a shuttle has to have at least 1 repeat
15566  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
15567  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
15568  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
15569  }
15570  }
15571  else if(ActionVectorEntry.SequenceType == Intermediate)
15572  {
15573  if(ActionVectorEntry.FormatType == TimeTimeLoc)
15574  {
15575  // here need 2 entries if times different so push the first right away & the second later
15576  // if times same just give the arrival entry
15577  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
15578  {
15579  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15580  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15581  OneTTEntry.Action = PartStr;
15582  OneTTEntry.Time = TimeStr;
15583  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15584  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15585  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
15586  }
15587  else
15588  {
15589  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
15590  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15591  }
15592  }
15593  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
15594  {
15595  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15596  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15597  }
15598  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
15599  {
15600  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15601  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
15602  }
15603  else if(ActionVectorEntry.FormatType == PassTime)
15604  {
15605  PartStr = "Passes " + ActionVectorEntry.LocationName;
15606  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
15607  }
15608  else if(ActionVectorEntry.Command == "jbo")
15609  {
15610  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
15611  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15612  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
15613  }
15614  else if(ActionVectorEntry.Command == "fsp")
15615  {
15616  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
15617  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15618  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
15619  }
15620  else if(ActionVectorEntry.Command == "rsp")
15621  {
15622  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
15623  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15624  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
15625  }
15626  else if(ActionVectorEntry.Command == "cdt")
15627  {
15628  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
15629  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
15630  }
15631  }
15632  else if(ActionVectorEntry.SequenceType == Finish)
15633  {
15634  if(ActionVectorEntry.Command == "Fns")
15635  {
15636  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15637  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15638  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
15639  }
15640  else if(ActionVectorEntry.Command == "F-nshs")
15641  {
15642  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15643  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15644  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
15645  }
15646  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15647  {
15648  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
15649  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15650  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
15651  // y+1 because it's the NEXT service repeat number that is relevant
15652  }
15653  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15654  {
15655  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15656  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15657  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
15658  }
15659  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15660  {
15661  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15662  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15663  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
15664  // y+1 because it's the NEXT service repeat number that is relevant
15665  }
15666  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15667  {
15668  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
15669  // only used in chronological tt
15670  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
15671  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
15672  }
15673  else if(ActionVectorEntry.Command == "Frh")
15674  {
15675  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
15676  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
15677  if(z > 0)
15678  // should be for finish entry but include check for safety
15679  {
15680  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
15681  {
15682  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
15683  }
15684  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
15685  {
15686  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
15687  }
15688  else
15689  {
15690  TimeStr = " "; // shouldn't ever get here
15691  }
15692  }
15693  }
15694  else if(ActionVectorEntry.Command == "Fer")
15695  {
15696  AnsiString AllowedExits;
15697  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
15698  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
15699  }
15700  else if(ActionVectorEntry.Command == "Fjo")
15701  {
15702  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
15703  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15704  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
15705  }
15706  }
15707  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
15708  {
15709  continue; // no entry needed for a repeat
15710  }
15711  OneTTEntry.Action = PartStr;
15712  OneTTEntry.Time = TimeStr;
15713  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15714  // one per action
15715  }
15716  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
15717  // one per repeat
15718  }
15719  AllTTTrains->push_back(OneTTLine); // one per repeating train
15720  }
15721  // AllTTTrains vector now complete
15722 
15723  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
15724 
15725  if(TTFile == 0)
15726  {
15727  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
15728  delete AllTTTrains;
15729  Utilities->CallLogPop(1567);
15730  return;
15731  }
15732 /* formatted timetable types
15733  class TOneTrainFormattedEntry
15734  {
15735  AnsiString Action;//includes location if relevant
15736  AnsiString Time;
15737  };
15738 
15739  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15740 
15741  class TOneCompleteFormattedTrain//headcode + list of actions
15742  {
15743  public:
15744  AnsiString HeadCode;
15745  TOneFormattedTrainVector OneFormattedTrainVector;
15746  };
15747 
15748  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15749 
15750  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15751  {
15752  public:
15753  AnsiString Header;//description, mass, power, brake rate etc
15754  int NumberOfTrains;// number of repeats + 1
15755  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15756  };
15757 
15758  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15759  //end of formatted timetable types
15760 */
15761 
15762  // new layout using multiple rows
15763  TTFile << TableTitle.c_str() << '\n' << '\n';
15764  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
15765  {
15766  TTFile << AllTTTrains->at(x).Header.c_str();
15767  TTFile << '\n';
15768  TTFile << ','; // for the blank line above the action list
15769  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15770  {
15771  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
15772  {
15773  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
15774  }
15775  else
15776  {
15777  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
15778  }
15779  }
15780  TTFile << '\n' << '\n';
15781 
15782  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
15783  {
15784  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
15785  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15786  {
15787  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
15788  {
15789  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
15790  }
15791  else
15792  {
15793  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
15794  }
15795  }
15796  TTFile << '\n';
15797  }
15798  TTFile << '\n' << '\n';
15799  }
15800 
15801  TTFile.close();
15802 
15803  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15804 
15805  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
15806 
15807  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
15808 
15809  if(TTFile2 == 0)
15810  {
15811  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
15812  delete AllTTTrains;
15813  Utilities->CallLogPop(1710);
15814  return;
15815  }
15816  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
15817  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
15818  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
15819 
15820  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
15821  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
15822 
15823  // multimap of AnsiStrings with TimeString as key (to sort automatically)
15824 
15825  TTFile2 << TableTitle.c_str() << '\n' << '\n';
15826  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
15827  {
15828  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15829  {
15830  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
15831  {
15832  bool GiveMessagesFalse = false;
15833  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
15834  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
15835  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
15836  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
15837  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
15838  {
15839  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
15840  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
15841  TimeString = TimeString.SubString(9, 5);
15842  ActionString += " " + OtherHeadCode;
15843  }
15844  if(TimeString.SubString(1, 7) == "End at ")
15845  // for Frh-sh final entry
15846  {
15847  TimeString = TimeString.SubString(8, 5);
15848  }
15849  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
15850  AnsiMultiMapEntry.first = TimeString;
15851  AnsiMultiMapEntry.second = OneLine;
15852  TAMM->insert(AnsiMultiMapEntry);
15853  }
15854  }
15855  }
15856 
15857  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
15858  {
15859  TTFile2 << (AMMIT->second).c_str();
15860  }
15861  delete AllTTTrains;
15862  delete TAMM;
15863  TTFile2.close();
15864  Utilities->CallLogPop(1580);
15865 }
15866 
15867 // ---------------------------------------------------------------------------
15868 
15869 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
15870  bool AtLocChecked, int ArrRange, int DepRange)
15871 {
15872 
15873  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
15874  bool AnalysisError = false;
15875 
15876  try
15877  {
15878  //New section at v2.5.0 for tt conflict analysis
15879  /*
15880  typedef std::list<AnsiString> TServiceCallingLocsList;
15881  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
15882 
15884  struct TLocServiceTimes
15885  {
15886  AnsiString Location;
15887  AnsiString ServiceAndRepeatNum;
15888  AnsiString AtLocTime;
15889  AnsiString ArrTime;
15890  AnsiString DepTime;
15891  AnsiString FrhMarker;
15892  };
15893  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
15894  */
15895 
15896  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
15897  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
15898 
15899  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
15900  TTrainDataVector::iterator TDVIt, TDVCopyIt;
15901  int Suffix = 0;
15902  int IteratorNumber = 0;
15903  AnsiString AnsiSuffix = "";
15904  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
15905  {
15906  IteratorNumber++; //first value in loop is 1
15907  Suffix = 0;
15908  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
15909  {
15910  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
15911  {
15912  Suffix++; //first value is 1
15913  AnsiSuffix = AnsiString(Suffix);
15914  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
15915  }
15916  }
15917  }
15918 
15919  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
15920  TServiceCallingLocsList ServiceCallingLocsList;
15921  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
15922  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15923  {
15924  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15925  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15926  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
15927  ServiceCallingLocsList.clear();
15928  if(ActionVector.empty())
15929  {
15930  continue;
15931  }
15932  if(ActionVector.at(0).SignallerControl)
15933  {
15934  continue;
15935  }
15936  for(unsigned int z = 0; z < ActionVector.size(); z++)
15937  {
15938  TActionVectorEntry AVE = ActionVector.at(z);
15939  if(AVE.FormatType == StartNew)
15940  {
15941  if(AVE.LocationType == AtLocation) //located Snt
15942  {
15943  ServiceCallingLocsList.push_back(AVE.LocationName);
15944  }
15945  else //unlocated Snt (could be entering at continuation)
15946  {
15948  if(TE.ActiveTrackElementName != "")
15949  {
15950  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
15951  }
15952  else
15953  {
15954  int HLoc = TE.HLoc;
15955  int VLoc = TE.VLoc;
15956  AnsiString HString;
15957  AnsiString VString;
15958  if(HLoc < 0)
15959  {
15960  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15961  }
15962  else
15963  {
15964  HString = AnsiString(HLoc);
15965  }
15966  if(VLoc < 0)
15967  {
15968  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15969  }
15970  else
15971  {
15972  VString = AnsiString(VLoc);
15973  }
15974  ServiceCallingLocsList.push_back(HString + '-' + VString);
15975  }
15976  }
15977  }
15978  else if(AVE.SequenceType == Start) //other start entries, all located
15979  {
15980  ServiceCallingLocsList.push_back(AVE.LocationName);
15981  }
15982  else if(AVE.FormatType == TimeLoc) //z must be > 0
15983  {
15984  if(ServiceCallingLocsList.back() != AVE.LocationName)
15985  {
15986  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
15987  }
15988  }
15989  else if(AVE.FormatType == PassTime)
15990  {
15991  ServiceCallingLocsList.push_back(AVE.LocationName);
15992  }
15993  else if(AVE.FormatType == TimeTimeLoc)
15994  {
15995  ServiceCallingLocsList.push_back(AVE.LocationName);
15996  }
15997  else if(AVE.Command == "cdt") //list if not next to start or finish
15998  {
15999  if(ActionVector.at(z-1).SequenceType == Start)
16000  {
16001  continue;
16002  }
16003  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
16004  {
16005  continue;
16006  }
16007  else
16008  {
16009  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
16010  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
16011  }
16012  }
16013  else if(AVE.FormatType == ExitRailway) //Fer
16014  {
16015  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
16016  AnsiString LName = TE.ActiveTrackElementName;
16017  if(LName != "")
16018  {
16019  ServiceCallingLocsList.push_back(LName);
16020  }
16021  else
16022  {
16023  int HLoc = TE.HLoc;
16024  int VLoc = TE.VLoc;
16025  AnsiString HString;
16026  AnsiString VString;
16027  if(HLoc < 0)
16028  {
16029  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16030  }
16031  else
16032  {
16033  HString = AnsiString(HLoc);
16034  }
16035  if(VLoc < 0)
16036  {
16037  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16038  }
16039  else
16040  {
16041  VString = AnsiString(VLoc);
16042  }
16043  ServiceCallingLocsList.push_back(HString + '-' + VString);
16044  }
16045  }
16046  }
16047  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
16048  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
16049  }
16050  //AllServiceCallingLocsMap built
16051 
16052  //test validity of AllServiceCallingLocsMap
16053 /*
16054  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16055  std::ofstream Test(TestFile.c_str());
16056 
16057  if(TestFile == 0)
16058  {
16059  ShowMessage("TestFile failed to open - can't be created");
16060  Utilities->CallLogPop();
16061  return false;
16062  }
16063 
16064  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
16065  {
16066  Test << ASCLIt->first << '\n'; //service ref
16067  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
16068  {
16069  Test << *SCLIt << '\n';
16070  }
16071  Test << "\n\n";
16072  }
16073  Test.close();
16074  Utilities->CallLogPop();
16075  return true;
16076 */
16077 
16078  //initialise variables before calc LastTTTime & build LocServiceTimesVector
16079  if(TrainDataVector.empty())
16080  {
16081  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
16082  Utilities->CallLogPop(2209);
16083  return(false);
16084  }
16085  TLocServiceTimes TLSTEntry;
16086  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
16087  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
16088  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
16089  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
16090  LastTTTime = "";
16091 
16092  //calculate LastTTTime
16093  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16094  {
16095  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16096  TActionVector &ActionVector = TrainDataEntry.ActionVector;
16097  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
16098  TDateTime LastTDTime;
16099  int IncMinutes = 0;
16100  NumTrains = TrainDataEntry.NumberOfTrains;
16101  if(ActionVector.empty())
16102  {
16103  continue;
16104  }
16105  if(ActionVector.at(0).SignallerControl)
16106  {
16107  continue;
16108  }
16109  if(AVLast->FormatType == Repeat)
16110  {
16111  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16112  AVLast--; //now points to the command before the repeat
16113  }
16114  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
16115  {
16116  AVLast--; //points to last timed entry
16117  }
16118  //here AVLast points to last entry with a time
16119  if(AVLast->ArrivalTime != TDateTime(-1))
16120  {
16121  LastTDTime = AVLast->ArrivalTime;
16122  }
16123  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
16124  {
16125  LastTDTime = AVLast->EventTime;
16126  }
16127  else
16128  {
16129  continue; //shouldn't ever reach here but if do then skip this service
16130  }
16131  if(NumTrains == 1)
16132  {
16133  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
16134  }
16135  else
16136  {
16137  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
16138  }
16139  if(LastAnsiTime > LastTTTime)
16140  {
16141  LastTTTime = LastAnsiTime;
16142  }
16143  }
16144 
16145 //build LocServiceTimesVector
16146 
16147 /*
16148  struct TLocServiceTimes
16149  {
16150  AnsiString Location;
16151  AnsiString ServiceAndRepeatNum;
16152  AnsiString AtLocTime;
16153  AnsiString ArrTime;
16154  AnsiString DepTime;
16155  AnsiString FrhMarker;
16156  };
16157  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16158 
16159 This works as follows:
16160 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
16161 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
16162 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
16163 
16164 Every action for every train is examined and times entered as follows:-
16165 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
16166 b) an unlocated Snt: entry time becomes DepTime
16167 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
16168 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
16169 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
16170 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
16171 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
16172 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
16173 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
16174 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
16175 */
16176  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16177  {
16178  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16179  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16180  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
16181  int IncMinutes = 0;
16182  NumTrains = TrainDataEntry.NumberOfTrains;
16183  if(ActionVector.empty())
16184  {
16185  continue;
16186  }
16187  if(ActionVector.at(0).SignallerControl)
16188  {
16189  continue;
16190  }
16191  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16192  {
16193  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16194  }
16195  for(int y = 0; y < NumTrains; y++)
16196  {
16197  if(NumTrains == 1)
16198  {
16199  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
16200  }
16201  else if(y == 0)
16202  {
16203  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
16204  }
16205  else
16206  {
16207  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
16208  }
16209  for(unsigned int z = 0; z < ActionVector.size(); z++)
16210  {
16211  TActionVectorEntry AVE = ActionVector.at(z);
16212  TLSTEntry.AtLocTime = "";
16213  TLSTEntry.ArrTime = "";
16214  TLSTEntry.DepTime = "";
16215  TLSTEntry.Location = "";
16216  TLSTEntry.FrhMarker = "";
16217 
16218  if(AVE.FormatType == StartNew) //Snt only
16219  {
16220  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
16221  {
16222  TLSTEntry.Location = AVE.LocationName;
16223  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
16224  LocServiceTimesVector.push_back(TLSTEntry);
16225 
16226  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16227  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
16228  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16229  {
16230  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16231  {
16232  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
16233  break;
16234  }
16235  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16236  {
16237  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
16238  break;
16239  }
16240  }
16241  if(FoundStopTime == "")
16242  {
16243  throw Exception("Failure to determine FoundStopTime for located Snt");
16244  }
16245  int WhileCount = 0;
16246  while(true)
16247  {
16248  //add minutes until reach FoundStopTime but don't add that time
16249  WhileCount++;
16250  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16251  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16252  TLSTEntry.DepTime = "";
16253  TLSTEntry.ArrTime = "";
16254  if(IncTime >= FoundStopTime) //don't add that time
16255  {
16256  break;
16257  }
16258  LocServiceTimesVector.push_back(TLSTEntry);
16259  if(WhileCount > 2000)
16260  {
16261  throw Exception("While loop failed to break in 2000 loops for located Snt");
16262  }
16263  }
16264  }
16265  else //unlocated Snt, use the EventTime as DepTime for this vector
16266  {
16268  if(TE.ActiveTrackElementName != "")
16269  {
16270  TLSTEntry.Location = TE.ActiveTrackElementName;
16271  }
16272  else
16273  {
16274  int HLoc = TE.HLoc;
16275  int VLoc = TE.VLoc;
16276  AnsiString HString;
16277  AnsiString VString;
16278  if(HLoc < 0)
16279  {
16280  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16281  }
16282  else
16283  {
16284  HString = AnsiString(HLoc);
16285  }
16286  if(VLoc < 0)
16287  {
16288  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16289  }
16290  else
16291  {
16292  VString = AnsiString(VLoc);
16293  }
16294  TLSTEntry.Location = HString + '-' + VString;
16295  }
16296  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
16297  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16298  LocServiceTimesVector.push_back(TLSTEntry);
16299  }
16300  }
16301 
16302  else if(AVE.SequenceType == Start) //other start entries, all located
16303  {
16304  TLSTEntry.Location = AVE.LocationName;
16305  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
16306  LocServiceTimesVector.push_back(TLSTEntry);
16307  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16308  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16309  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16310  {
16311  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16312  {
16313  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
16314  break;
16315  }
16316  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16317  {
16318  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
16319  break;
16320  }
16321  }
16322  if(FoundStopTime == "")
16323  {
16324  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16325  }
16326  int WhileCount = 0;
16327  while(true)
16328  {
16329  //add minutes until reach FoundStopTime but don't add that time
16330  WhileCount++;
16331  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16332  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16333  TLSTEntry.DepTime = "";
16334  TLSTEntry.ArrTime = "";
16335  if(IncTime >= FoundStopTime) //don't add that time
16336  {
16337  break;
16338  }
16339  LocServiceTimesVector.push_back(TLSTEntry);
16340  if(WhileCount > 2000)
16341  {
16342  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16343  }
16344  }
16345  }
16346 
16347  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
16348  {
16349  TLSTEntry.Location = AVE.LocationName;
16350  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
16351  {
16352  bool SkipAddingMinutes = false;
16353  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
16354  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16355  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
16356  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16357  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16358  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16359  {
16360  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16361  {
16362  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
16363  break;
16364  }
16365  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16366  {
16367  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
16368  if((a == z + 1) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
16369  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
16370  {
16371  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
16372  SkipAddingMinutes = true;
16373  }
16374  break;
16375  }
16376  }
16377  if(FoundStopTime == "")
16378  {
16379  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16380  }
16381  if(!SkipAddingMinutes)
16382  {
16383  int WhileCount = 0;
16384  while(true)
16385  {
16386  //add minutes until reach FoundStopTime but don't add that time
16387  WhileCount++;
16388  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16389  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16390  TLSTEntry.DepTime = "";
16391  TLSTEntry.ArrTime = "";
16392  if(IncTime >= FoundStopTime) //don't add that time
16393  {
16394  break;
16395  }
16396  LocServiceTimesVector.push_back(TLSTEntry);
16397  if(WhileCount > 2000)
16398  {
16399  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16400  }
16401  }
16402  }
16403  }
16404  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
16405  {
16406  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
16407  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16408  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
16409  {
16410  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
16411  {
16412  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
16413  LocServiceTimesVector.pop_back();
16414  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
16415  }
16416  else //just add the dep & atloc times
16417  {
16418  TLSTEntry.ArrTime = "";
16419  LocServiceTimesVector.push_back(TLSTEntry);
16420  }
16421  }
16422  else //just add the dep & atloc times
16423  {
16424  TLSTEntry.ArrTime = "";
16425  LocServiceTimesVector.push_back(TLSTEntry);
16426  }
16427  }
16428  }
16429 
16430  else if(AVE.FormatType == TimeTimeLoc)
16431  {
16432  TLSTEntry.Location = AVE.LocationName;
16433  if(AVE.ArrivalTime > TDateTime(-1)) //should be
16434  {
16435  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
16436  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16437  }
16438  if(AVE.DepartureTime > TDateTime(-1)) //should be
16439  {
16440  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
16441  }
16442  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
16443  {
16444  LocServiceTimesVector.push_back(TLSTEntry);
16445  }
16446  else
16447  {
16448  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
16449  TLSTEntry.DepTime = "";
16450  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
16451  TLSTEntry.ArrTime = ""; //done with this now
16452  while(TLSTEntry.AtLocTime < TempDepTime)
16453  {
16454  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16455  if(TLSTEntry.AtLocTime == TempDepTime)
16456  {
16457  TLSTEntry.DepTime = TempDepTime; //restore value
16458  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
16459  }
16460  else
16461  {
16462  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
16463  }
16464  }
16465  }
16466  }
16467 
16468  else if(AVE.FormatType == PassTime) //added at v2.9.1
16469  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
16470  TLSTEntry.Location = AVE.LocationName;;
16471  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
16472  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
16473  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
16474  TLSTEntry.ArrTime = ""; //need to reset this to null
16475  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
16476  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
16477  }
16478 
16479  else if(AVE.FormatType == ExitRailway) //Fer
16480  {
16481  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
16482  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
16483  //be wrong, but can't guess from here & most will have same name
16484  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
16485  if(LName != "")
16486  {
16487  TLSTEntry.Location = LName;
16488  }
16489  else
16490  {
16491  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
16492  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
16493  AnsiString HString;
16494  AnsiString VString;
16495  if(HLoc < 0)
16496  {
16497  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16498  }
16499  else
16500  {
16501  HString = AnsiString(HLoc);
16502  }
16503  if(VLoc < 0)
16504  {
16505  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16506  }
16507  else
16508  {
16509  VString = AnsiString(VLoc);
16510  }
16511  TLSTEntry.Location = HString + '-' + VString;
16512  }
16513  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
16514  }
16515 
16516  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
16517  {
16518  AnsiString FrhTime;
16519  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16520  {
16521  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16522  }
16523  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16524  {
16525  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16526  }
16527  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
16528  TLSTEntry.Location = AVE.LocationName;
16529  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16530  TLSTEntry.FrhMarker = "Frh";
16531  LocServiceTimesVector.push_back(TLSTEntry);
16532  TLSTEntry.FrhMarker = "";
16533  //add all times from next minute to end of timetable
16534  while(IncTime <= LastTTTime)
16535  {
16536  TLSTEntry.AtLocTime = IncTime;
16537  LocServiceTimesVector.push_back(TLSTEntry);
16538  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16539  }
16540  }
16541 
16542  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
16543  {
16544  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
16545  {
16546  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
16547  TLSTEntry.Location = AVE.LocationName;
16548  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16549  TLSTEntry.FrhMarker = "Frh";
16550  LocServiceTimesVector.push_back(TLSTEntry);
16551  TLSTEntry.FrhMarker = "";
16552  //add all times from next minute to end of timetable
16553  while(IncTime <= LastTTTime)
16554  {
16555  TLSTEntry.AtLocTime = IncTime;
16556  LocServiceTimesVector.push_back(TLSTEntry);
16557  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16558  }
16559  }
16560  }
16561 
16562  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
16563  {
16564  //nothing is done here as the entry will be listed at this time under the new service reference
16565  }
16566  }
16567  }
16568  }
16569 
16570  //now sort in location order
16571  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
16572  //LocServiceTimesVector now complete & sorted in location order
16573 
16575 /*
16576  std::ofstream LSTVFile("LSTVFile.txt");
16577  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
16578  {
16579  LSTVFile << LSTVIt->Location + '\n';
16580  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
16581  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
16582  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
16583  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
16584  if(LSTVIt->FrhMarker == "")
16585  {
16586  LSTVFile << "Not Frh\n";
16587  }
16588  else
16589  {
16590  LSTVFile << LSTVIt->FrhMarker + '\n';
16591  }
16592  LSTVFile << '\n';
16593  }
16594  LSTVFile.close();
16595 */
16596  //declare pointers for use in printouts
16597  TLocServiceTimesVector::iterator Ptr1, Ptr2;
16598 
16599  //set up the output file
16600  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16601  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
16602 
16603  std::ofstream TTFile3(TTFileName3.c_str());
16604 
16605  if(TTFile3 == 0)
16606  {
16607  ShowMessage("Conflict Analysis file failed to open - can't be created");
16608  Utilities->CallLogPop(2210);
16609  return(false);
16610  }
16611  if(LocServiceTimesVector.empty())
16612  {
16613  ShowMessage("No timetabled services found");
16614  TTFile3.close();
16615  DeleteFile(TTFileName3);
16616  Utilities->CallLogPop(2211);
16617  return(false);
16618  }
16619  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n\n\n";
16620 
16621 
16622  //arrivals
16623  if(ArrChecked)
16624  {
16625  //sort in ArrTime order for each location
16626  Ptr1 = LocServiceTimesVector.begin();
16627  Ptr2 = Ptr1 + 1;
16628  while(Ptr2 != LocServiceTimesVector.end())
16629  {
16630  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16631  {
16632  Ptr2++;
16633  if(Ptr2 == LocServiceTimesVector.end())
16634  {
16635  break;
16636  }
16637  }
16638  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
16639  Ptr1 = Ptr2; //first entry with next name
16640  if(Ptr2 != LocServiceTimesVector.end())
16641  {
16642  Ptr2++;
16643  }
16644  }
16645 
16646  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
16647 
16648  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
16649  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16650  MinuteString = " minutes";
16651  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16652  if(ArrRange == 1)
16653  {
16654  MinuteString = " minute";
16655  }
16656  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
16657  TTFile3 << ",Platforms,Trains\n\n";
16658 
16659  Ptr1 = LocServiceTimesVector.begin();
16660  Ptr2 = Ptr1 + 1;
16661  while(Ptr2 != LocServiceTimesVector.end())
16662  {
16663  PreviousService = "";
16664  NumTrainsAtLoc = 0;
16665  ServiceAndRepeatNumTotal = "";
16666  NumPlats = 0;
16667  NumPlatsAtThisLocCalculated = false;
16668  BasicTime = "";
16669  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16670  {
16671  PreviousService = "";
16672  NumTrainsAtLoc = 0;
16673  ServiceAndRepeatNumTotal = "";
16674  NumPlats = 0;
16675  NumPlatsAtThisLocCalculated = false;
16676  BasicTime = "";
16677  Ptr1++;
16678  Ptr2++;
16679  if(Ptr2 == LocServiceTimesVector.end())
16680  {
16681  break;
16682  }
16683  }
16684  if(Ptr2 == LocServiceTimesVector.end())
16685  {
16686  break;
16687  }
16688  while(Ptr2->Location == Ptr1->Location)
16689  {
16690  PreviousService = "";
16691  NumTrainsAtLoc = 0;
16692  ServiceAndRepeatNumTotal = "";
16693  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
16694  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16695  {
16696  break;
16697  }
16698  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
16699  {
16700  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
16701  Ptr1++;
16702  Ptr2++;
16703  if(Ptr2 == LocServiceTimesVector.end())
16704  {
16705  break;
16706  }
16707  if(Ptr2->Location != Ptr1->Location)
16708  {
16709  break;
16710  }
16711  }
16712  if(Ptr2 == LocServiceTimesVector.end())
16713  {
16714  break;
16715  }
16716  if(Ptr2->Location != Ptr1->Location)
16717  {
16718  break;
16719  }
16720  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
16721  {
16722  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
16723  {
16724  break;
16725  }
16726  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16727  {
16728  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
16729  NumPlatsAtThisLocCalculated = true;
16730  }
16731  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16732  {
16733  if(ServiceAndRepeatNumTotal == "")
16734  {
16735  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
16736  NumTrainsAtLoc = 1;
16737  }
16738  else
16739  {
16740  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
16741  }
16742  }
16743  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16744  if(ServiceAndRepeatNumTotal == "")
16745  {
16746  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
16747  NumTrainsAtLoc = 1;
16748  }
16749  else
16750  {
16751  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
16752  }
16753  Ptr1 = Ptr2;
16754  Ptr2++;
16755  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
16756  {
16757  int MaxNumberOfSameDirections = 0;
16758  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16759  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16760  {
16761 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16762  TTFile3.close();
16763  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16764 // Utilities->CallLogPop(2224);
16765 // return false;
16766  }
16767  AnsiString Asterisk = "";
16768  if(MaxNumberOfSameDirections >= NumPlats)
16769  {
16770  Asterisk = "* ";
16771  }
16772  //print out a single line for number of trains at loc with all service refs
16773  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16774  ArrivalsPrinted = true;
16775  ServiceAndRepeatNumTotal = "";
16776  }
16777  if(Ptr2 == LocServiceTimesVector.end())
16778  {
16779  break;
16780  }
16781  if(Ptr2->Location != Ptr1->Location)
16782  {
16783  break;
16784  }
16785  }
16786  if(Ptr2 == LocServiceTimesVector.end())
16787  {
16788  break;
16789  }
16790  }
16791  }
16792  if(!ArrivalsPrinted)
16793  {
16794  TTFile3 << "Nothing to report for arrivals";
16795  }
16796  TTFile3 << "\n\n\n";
16797  }
16798  //end of routine for arrivals
16799 
16800  //departures
16801  if(DepChecked)
16802  {
16803  //sort in DepTime order for each location
16804  Ptr1 = LocServiceTimesVector.begin();
16805  Ptr2 = Ptr1 + 1;
16806  while(Ptr2 != LocServiceTimesVector.end())
16807  {
16808  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16809  {
16810  Ptr2++;
16811  if(Ptr2 == LocServiceTimesVector.end())
16812  {
16813  break;
16814  }
16815  }
16816  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
16817  Ptr1 = Ptr2; //first entry with next name
16818  if(Ptr2 != LocServiceTimesVector.end())
16819  {
16820  Ptr2++;
16821  }
16822  }
16823 
16824  //routine for departures - number of trains departing within the specified range with services listed at the end
16825  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
16826  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16827  MinuteString = " minutes";
16828  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16829  if(DepRange == 1)
16830  {
16831  MinuteString = " minute";
16832  }
16833  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
16834  TTFile3 << ",Platforms,Trains\n\n";
16835 
16836  Ptr1 = LocServiceTimesVector.begin();
16837  Ptr2 = Ptr1 + 1;
16838  while(Ptr2 != LocServiceTimesVector.end())
16839  {
16840  PreviousService = "";
16841  NumTrainsAtLoc = 0;
16842  ServiceAndRepeatNumTotal = "";
16843  NumPlats = 0;
16844  NumPlatsAtThisLocCalculated = false;
16845  BasicTime = "";
16846  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16847  {
16848  PreviousService = "";
16849  NumTrainsAtLoc = 0;
16850  ServiceAndRepeatNumTotal = "";
16851  NumPlats = 0;
16852  NumPlatsAtThisLocCalculated = false;
16853  BasicTime = "";
16854  Ptr1++;
16855  Ptr2++;
16856  if(Ptr2 == LocServiceTimesVector.end())
16857  {
16858  break;
16859  }
16860  }
16861  if(Ptr2 == LocServiceTimesVector.end())
16862  {
16863  break;
16864  }
16865  while(Ptr2->Location == Ptr1->Location)
16866  {
16867  PreviousService = "";
16868  NumTrainsAtLoc = 0;
16869  ServiceAndRepeatNumTotal = "";
16870  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
16871  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16872  {
16873  break;
16874  }
16875  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
16876  {
16877  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
16878  Ptr1++;
16879  Ptr2++;
16880  if(Ptr2 == LocServiceTimesVector.end())
16881  {
16882  break;
16883  }
16884  if(Ptr2->Location != Ptr1->Location)
16885  {
16886  break;
16887  }
16888  }
16889  if(Ptr2 == LocServiceTimesVector.end())
16890  {
16891  break;
16892  }
16893  if(Ptr2->Location != Ptr1->Location)
16894  {
16895  break;
16896  }
16897  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
16898  {
16899  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
16900  {
16901  break;
16902  }
16903  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16904  {
16905  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
16906  NumPlatsAtThisLocCalculated = true;
16907  }
16908  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16909  {
16910  if(ServiceAndRepeatNumTotal == "")
16911  {
16912  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16913  NumTrainsAtLoc = 1;
16914  }
16915  else
16916  {
16917  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16918  }
16919  }
16920  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16921  if(ServiceAndRepeatNumTotal == "")
16922  {
16923  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16924  NumTrainsAtLoc = 1;
16925  }
16926  else
16927  {
16928  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16929  }
16930  Ptr1 = Ptr2;
16931  Ptr2++;
16932  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
16933  {
16934  int MaxNumberOfSameDirections = 0;
16935  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16936  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16937  {
16938 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16939  TTFile3.close();
16940  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16941 // Utilities->CallLogPop(2225);
16942 // return false;
16943  }
16944  AnsiString Asterisk = "";
16945  if(MaxNumberOfSameDirections >= NumPlats)
16946  {
16947  Asterisk = "* ";
16948  }
16949  //print out a single line for number of trains at loc with all service refs
16950  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16951  DeparturesPrinted = true;
16952  ServiceAndRepeatNumTotal = "";
16953  }
16954  if(Ptr2 == LocServiceTimesVector.end())
16955  {
16956  break;
16957  }
16958  if(Ptr2->Location != Ptr1->Location)
16959  {
16960  break;
16961  }
16962  }
16963  if(Ptr2 == LocServiceTimesVector.end())
16964  {
16965  break;
16966  }
16967  }
16968  }
16969  if(!DeparturesPrinted)
16970  {
16971  TTFile3 << "Nothing to report for departures";
16972  }
16973  TTFile3 << "\n\n\n";
16974  }
16975  //end of routine for departures
16976 
16977 
16978  //list trains at locations at same time
16979 
16980  if(AtLocChecked)
16981  {
16982  //sort in AtLocTime order for each location
16983  Ptr1 = LocServiceTimesVector.begin();
16984  Ptr2 = Ptr1 + 1;
16985  while(Ptr2 != LocServiceTimesVector.end())
16986  {
16987  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16988  {
16989  Ptr2++;
16990  if(Ptr2 == LocServiceTimesVector.end())
16991  {
16992  break;
16993  }
16994  }
16995  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
16996  Ptr1 = Ptr2; //first entry with next name
16997  if(Ptr2 != LocServiceTimesVector.end())
16998  {
16999  Ptr2++;
17000  }
17001  }
17002 
17003  //print out simultaneous AtLocs (don't need range of times for AtLocs)
17004  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
17005  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
17006  TTFile3 << ",Platforms,Trains,\n\n";
17007  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17008  Ptr1 = LocServiceTimesVector.begin();
17009  Ptr2 = Ptr1 + 1;
17010  while(Ptr2 != LocServiceTimesVector.end())
17011  {
17012  PreviousService = "";
17013  ServiceAndRepeatNumTotal = "";
17014  NumTrainsAtLoc = 0;
17015  NumPlats = 0;
17016  NumPlatsAtThisLocCalculated = false;
17017  FrhCount = 0;
17018  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17019  {
17020  PreviousService = "";
17021  ServiceAndRepeatNumTotal = "";
17022  NumTrainsAtLoc = 0;
17023  NumPlats = 0;
17024  NumPlatsAtThisLocCalculated = false;
17025  FrhCount = 0;
17026  Ptr1++;
17027  Ptr2++;
17028  if(Ptr2 == LocServiceTimesVector.end())
17029  {
17030  break;
17031  }
17032  }
17033  if(Ptr2 == LocServiceTimesVector.end())
17034  {
17035  break;
17036  }
17037  while(Ptr2->Location == Ptr1->Location)
17038  {
17039  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
17040  {
17041  FrhCount++;
17042  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17043  }
17044  PreviousService = "";
17045  NumTrainsAtLoc = 0;
17046  ServiceAndRepeatNumTotal = "";
17047  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17048  {
17049  break;
17050  }
17051  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
17052  {
17053  Ptr1++;
17054  if(Ptr1->FrhMarker == "Frh")
17055  {
17056  FrhCount++;
17057  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17058  }
17059  Ptr2++;
17060  if(Ptr2 == LocServiceTimesVector.end())
17061  {
17062  break;
17063  }
17064  if(Ptr2->Location != Ptr1->Location)
17065  {
17066  break;
17067  }
17068  }
17069  if(Ptr2 == LocServiceTimesVector.end())
17070  {
17071  break;
17072  }
17073  if(Ptr2->Location != Ptr1->Location)
17074  {
17075  break;
17076  }
17077  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
17078  {
17079  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
17080  {
17081  break;
17082  }
17083  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17084  {
17085  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
17086  NumPlatsAtThisLocCalculated = true;
17087  }
17088  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17089  {
17090  if(ServiceAndRepeatNumTotal == "")
17091  {
17092  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
17093  NumTrainsAtLoc = 1;
17094  }
17095  else
17096  {
17097  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
17098  }
17099  }
17100  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
17101  if(ServiceAndRepeatNumTotal == "")
17102  {
17103  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
17104  NumTrainsAtLoc = 1;
17105  }
17106  else
17107  {
17108  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
17109  }
17110  Ptr1 = Ptr2;
17111  if(Ptr1->FrhMarker == "Frh")
17112  {
17113  FrhCount++;
17114  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17115  }
17116  Ptr2++;
17117  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
17118  {
17119 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
17120 //new text //don't print out if all remainers or if only 1 train at loc
17121  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
17122 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
17123 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
17124  {
17125  AnsiString Asterisk = "";
17126  if(NumTrainsAtLoc > NumPlats)
17127  {
17128  Asterisk = "* ";
17129  }
17130  //print out a single line for number of trains at loc with all service refs
17131  if(FrhCount == 0)
17132  {
17133  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
17134  }
17135  else if(FrhCount == 1)
17136  {
17137  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
17138  }
17139  else
17140  {
17141  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
17142  }
17143  LastFrhCount = FrhCount;
17144  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
17145  AtLocsPrinted = true;
17146  ServiceAndRepeatNumTotal = "";
17147  }
17148  }
17149  if(Ptr2 == LocServiceTimesVector.end())
17150  {
17151  break;
17152  }
17153  if(Ptr2->Location != Ptr1->Location)
17154  {
17155  break;
17156  }
17157  }
17158  if(Ptr2 == LocServiceTimesVector.end())
17159  {
17160  break;
17161  }
17162  }
17163  }
17164  if(!AtLocsPrinted)
17165  {
17166  TTFile3 << "Nothing to report for trains at locations";
17167  }
17168  TTFile3 << "\n\n\n";
17169  //end of simultaneous AtLocs
17170 
17171 /*
17172  //print out the full vector here for testing purposes
17173  TTFile3 << "Full LocServiceTimesVector\n\n";
17174  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
17175 
17176  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
17177  {
17178  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
17179  }
17180 
17181  TTFile3 << "\n\n\n";
17182 */
17183  }
17184  TTFile3.close();
17185  Utilities->CallLogPop(2212);
17186  return(true);
17187  }
17188 
17189  catch(const Exception &e) //non error catch
17190  {
17191  AnsiString TTErrorFileName = "Analysis Error.txt";
17192  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
17193  std::ofstream TTError(TTErrorFileName.c_str());
17194  if(TTError == 0)
17195  {
17196  ShowMessage("Analysis error file failed to open - can't be created");
17197  Utilities->CallLogPop(2233);
17198  return(false);
17199  }
17200  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17201  TTError << TimeNow.c_str() << "\n" << ArrRange << "\n" << ArrChecked << "\n" << DepRange << "\n" <<
17202  DepChecked << "\n" << AtLocChecked << "\n" << AnsiString(e.Message);
17203  TTError.close();
17204  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
17205  Utilities->CallLogPop(2226);
17206  return(false);
17207  }
17208 }
17209 
17210 // ---------------------------------------------------------------------------
17211 
17212 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
17213 {
17214 //convert times to integer minutes
17215  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
17216  if((Time1 == "") || (Time2 == ""))
17217  {
17218  Utilities->CallLogPop(2213);
17219  return(false);
17220  }
17221  int Mins = Time1.SubString(4,2).ToInt();
17222  int Hours = Time1.SubString(1,2).ToInt();
17223  int Time1Mins = (Hours * 60) + Mins;
17224  Mins = Time2.SubString(4,2).ToInt();
17225  Hours = Time2.SubString(1,2).ToInt();
17226  int Time2Mins = (Hours * 60) + Mins;
17227  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
17228  {
17229  Utilities->CallLogPop(2214);
17230  return(true);
17231  }
17232  Utilities->CallLogPop(2215);
17233  return(false);
17234 }
17235 
17236 // ---------------------------------------------------------------------------
17237 
17238 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
17239  bool &AnalysisError, int &MaxNumberOfSameDirections)
17240 {
17241  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
17242 
17243  try
17244  {
17245  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
17246  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
17247  int SCPos = 0;
17248  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
17249  //first change every second comma in Input to a semicolon so can separate services but keep times with services
17250  bool EvenComma = false;
17251  for(int x = 1; x <= Input.Length(); x++)
17252  {
17253  TempStr1 = Input[x];
17254  if(TempStr1 == AnsiString(',') && EvenComma)
17255  {
17256  TempStr2 += ';';
17257  }
17258  else
17259  {
17260  TempStr2 += Input[x];
17261  }
17262  if(TempStr1 == AnsiString(','))
17263  {
17264  EvenComma = !EvenComma;
17265  }
17266  }
17267  //load up the list of services with associated times
17268  while(TempStr2.Length() > 0)
17269  {
17270  SCPos = TempStr2.Pos(';');
17271  if(SCPos > 0) //0 if not found, as won't be when only one service left
17272  {
17273  OneService = TempStr2.SubString(1, SCPos - 1);
17274  ServiceList.push_back(OneService);
17275  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
17276  }
17277  else //no semicolon so looking at last (or only) element
17278  {
17279  ServiceList.push_back(TempStr2);
17280  TempStr2 = "";
17281  }
17282  }
17283  ServiceList.sort(); // alphabetical order
17284  ServiceList.unique(); //remove duplicates
17285  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
17286 
17287  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
17288  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
17289  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
17290  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
17291 
17292  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17293  {
17294  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
17295  }
17296  SLIt3 = ServiceList.end();
17297  SLIt3--; //so points to last element
17298  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
17299  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
17300  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
17301  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
17302  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
17303  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
17304 
17305  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
17306  {
17307  SLIt = SLIt1;
17308  SLIt++; //so points to one after SLIt1
17309  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
17310  {
17311  continue; //already allocated so skip to the next
17312  }
17313  else
17314  {
17315  CommaPos1 = SLIt1->Pos(','); //can't be 0
17316  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
17317  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
17318  SpacePos = ServiceRef1.Pos(' ');
17319  RepeatNum1 = 0;
17320  if(SpacePos > 0) //otherwise it's already correct
17321  {
17322  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
17323  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
17324  if(RepeatInfo1[1] == 'F')
17325  {
17326  RepeatNum1 = 0;
17327  }
17328  else
17329  {
17330  SpacePos = RepeatInfo1.Pos(' ');
17331  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
17332  }
17333  }
17334  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
17335  //but this includes the "&0" etc so need to strip these
17336  AmpersandPos = AnsiTime1.Pos('&');
17337  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
17338 
17339  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
17340  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
17341  {
17342  throw Exception("ASCLIt1 Error in " + Input);
17343  }
17344  ServiceCallingLocsList1 = ASCLIt1->second;
17345  AmpersandPos = SLIt1->Pos('&');
17346  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17347  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
17348 
17349  SameDirectionCount = 1;
17350  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
17351  {
17352  CommaPos2 = SLIt2->Pos(','); //can't be 0
17353  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
17354  //but this contains "(First service..." etc so need to strip these
17355  SpacePos = ServiceRef2.Pos(' ');
17356  RepeatNum2 = 0;
17357  if(SpacePos > 0) //otherwise it's already correct
17358  {
17359  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
17360  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
17361  if(RepeatInfo2[1] == 'F')
17362  {
17363  RepeatNum2 = 0;
17364  }
17365  else
17366  {
17367  SpacePos = RepeatInfo2.Pos(' ');
17368  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
17369  }
17370  }
17371  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
17372  //but this includes the "&0" etc so need to strip these
17373  AmpersandPos = AnsiTime2.Pos('&');
17374  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
17375 
17376  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
17377  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
17378  {
17379  throw Exception("ASCLIt2 Error in " + Input);
17380  }
17381  ServiceCallingLocsList2 = ASCLIt2->second;
17382  //now compare the two
17383  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
17384  {
17385  int AmpersandPos = SLIt2->Pos('&');
17386  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17387  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
17388  SameDirectionCount++;
17389  }
17390  }
17391  if(SameDirectionCount > MaxNumberOfSameDirections)
17392  {
17393  MaxNumberOfSameDirections = SameDirectionCount;
17394  }
17395  }
17396  }
17397 
17398  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
17399  {
17400  //any existing direction so allocate it now
17401  AmpersandPos = SLIt3->Pos('&');
17402  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17403  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
17404  }
17405  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
17406  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17407  {
17408  //extract the DirectionMarker as an integer
17409  AmpersandPos = SLIt->Pos('&');
17410  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
17411  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
17412  DirectionMarker = DirectionMarkerString.ToInt();
17413  AnsiString DirectionSuffix = "";
17414  char c;
17415  if(DirectionMarker < 27)
17416  {
17417  c = 64 + DirectionMarker; //so 1 -> 'A'
17418  DirectionSuffix = "," + AnsiString(c);
17419  }
17420  else if(DirectionMarker < 53)
17421  {
17422  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
17423  DirectionSuffix = ",A" + AnsiString(c);
17424  }
17425  else
17426  {
17427  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
17428  }
17429  *SLIt = ServiceWithoutMarker + DirectionSuffix;
17430  }
17431  //now prepare the final consolidated output
17432  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17433  {
17434  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
17435  }
17436  if(Output.Length() > 0)
17437  {
17438  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
17439  }
17440  Utilities->CallLogPop(2216);
17441  return(Output);
17442  }
17443 
17444  catch(const Exception &e) //non error catch
17445  {
17446  AnalysisError = true;
17447  Utilities->CallLogPop(2227);
17448  return(e.Message);
17449  }
17450 }
17451 
17452 // ---------------------------------------------------------------------------
17453 
17454 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
17455 {
17456  //similar to above but doesn't include times in the input
17457  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
17458  AnsiString InternalInput = Input, Output = "", OneService = "";
17459  int CommaPos = 0;
17460  std::list<AnsiString> ServiceList;
17461  //load up the list
17462  while(InternalInput.Length() > 0)
17463  {
17464  CommaPos = InternalInput.Pos(',');
17465  if(CommaPos > 0) //0 if not found, as won't be when only one service left
17466  {
17467  OneService = InternalInput.SubString(1, CommaPos - 1);
17468  ServiceList.push_back(OneService);
17469  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
17470  }
17471  else //no comma so looking at last (or only) element
17472  {
17473  ServiceList.push_back(InternalInput);
17474  InternalInput = "";
17475  }
17476  }
17477 
17478  ServiceList.sort(); // alphabetical order
17479  ServiceList.unique(); //remove duplicates
17480  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
17481  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17482  {
17483  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
17484  }
17485  if(Output.Length() > 0)
17486  {
17487  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
17488  }
17489  Utilities->CallLogPop(2217);
17490  return(Output);
17491 }
17492 
17493 // ---------------------------------------------------------------------------
17494 
17495 
17496 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
17497  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
17498 {
17499  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
17500  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
17501 
17502  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
17503  //general list pointers, LocPtrs point to Location in the two lists
17504 
17505  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
17506  //for List1
17507  bool LocFound = false;
17508  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
17509  int IncMinutes;
17510  TDateTime FirstServiceTime;
17511 
17512  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
17513  int Ref1Target = 0, Ref1Count = 0;
17514  int SlashPos = Ref1.Pos('/');
17515  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
17516  {
17517  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
17518  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
17519  }
17520  int Ref2Target = 0, Ref2Count = 0;
17521  SlashPos = Ref2.Pos('/');
17522  if(SlashPos > 0) //if 0 leave as is
17523  {
17524  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
17525  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
17526  }
17527  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
17528  {
17529  //even if others have same names. But if there are cdt's then need to refind the correct service
17530  if((*ListPtr1) == Location) //
17531  {
17532  LocPtr1 = ListPtr1; //may be modified later
17533  LocFound = true;
17534  }
17535  if(ListPtr1->SubString(1, 3) == "%%%")
17536  {
17537  AnsiString CDTTime = ListPtr1->SubString(4, 5);
17538  //now adjust the time to correspond to the repeat if there is one
17539  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
17540  {
17541  IncMinutes = -1;
17542  FirstServiceTime = TDateTime(-1);
17543  bool BreakFlag = false;
17544  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
17545  {
17546  if(TDVIt->ServiceReference == Ref1)
17547  {
17548  if(Ref1Target > Ref1Count)
17549  {
17550  Ref1Count++;
17551  continue;
17552  }
17553  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
17554  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
17555  {
17556  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
17557  {
17558  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
17559  BreakFlag = true;
17560  break;
17561  }
17562  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
17563  {
17564  FirstServiceTime = AVIt->ArrivalTime;
17565  BreakFlag = true;
17566  break;
17567  }
17568  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
17569  {
17570  FirstServiceTime = AVIt->DepartureTime;
17571  BreakFlag = true;
17572  break;
17573  }
17574  }
17575  if(BreakFlag)
17576  {
17577  break;
17578  }
17579  }
17580  }
17581  if(IncMinutes == -1)
17582  {
17583  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17584  }
17585  if(FirstServiceTime == TDateTime(-1))
17586  {
17587  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17588  }
17589  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
17590  }
17591  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
17592  {
17593  LocFound = false;
17594  continue;
17595  }
17596  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
17597  {
17598  break;
17599  }
17600  if(Time1 > CDTTime) //not there yet so go on
17601  {
17602  LocFound = false;
17603  continue;
17604  }
17605  if(Time1 < CDTTime) //gone too far so can stop now
17606  {
17607  break;
17608  }
17609  }
17610  }
17611  if(!LocFound) //have to find it in both lists
17612  {
17613  Utilities->CallLogPop(2228);
17614  return( false);
17615  }
17616  //for List2
17617  LocFound = false;
17618  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
17619  {
17620  if((*ListPtr2) == Location)
17621  {
17622  LocPtr2 = ListPtr2; //may be modified later
17623  LocFound = true;
17624  }
17625  if(ListPtr2->SubString(1, 3) == "%%%")
17626  {
17627  AnsiString CDTTime = ListPtr2->SubString(4, 5);
17628  //now adjust the time to correspond to the repeat if there is one
17629  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
17630  {
17631  IncMinutes = -1;
17632  FirstServiceTime = TDateTime(-1);
17633  bool BreakFlag = false;
17634  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
17635  {
17636  if(TDVIt->ServiceReference == Ref2)
17637  {
17638  if(Ref2Target > Ref2Count)
17639  {
17640  Ref2Count++;
17641  continue;
17642  }
17643  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
17644  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
17645  {
17646  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
17647  {
17648  FirstServiceTime = AVIt->EventTime;
17649  BreakFlag = true;
17650  break;
17651  }
17652  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
17653  {
17654  FirstServiceTime = AVIt->ArrivalTime;
17655  BreakFlag = true;
17656  break;
17657  }
17658  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
17659  {
17660  FirstServiceTime = AVIt->DepartureTime;
17661  BreakFlag = true;
17662  break;
17663  }
17664  }
17665  if(BreakFlag)
17666  {
17667  break;
17668  }
17669  }
17670  }
17671  if(IncMinutes == -1)
17672  {
17673  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17674  }
17675  if(FirstServiceTime == TDateTime(-1))
17676  {
17677  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17678  }
17679  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
17680  }
17681  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
17682  {
17683  LocFound = false;
17684  continue;
17685  }
17686  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
17687  {
17688  break;
17689  }
17690  if(Time2 > CDTTime) //not there yet so go on
17691  {
17692  LocFound = false;
17693  continue;
17694  }
17695  if(Time2 < CDTTime) //gone too far so can stop now
17696  {
17697  break;
17698  }
17699  }
17700  }
17701  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
17702  {
17703  Utilities->CallLogPop(2229);
17704  return( false);
17705  }
17706  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
17707  //set ListPtr1 to the search start position
17708  if(Arrival)
17709  {
17710  LP1 = List1.begin();
17711  LP1--; //now points to before the first entry
17712  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
17713  {
17714  if(ListPtr1 == List1.begin())
17715  {
17716  break;
17717  }
17718  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
17719  {
17720  ListPtr1++; //point to one past the cdt
17721  break;
17722  }
17723  }
17724  //set ListPtr2 to the search start position
17725  LP2 = List2.begin();
17726  LP2--; //now points to before the first entry
17727  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
17728  {
17729  if(ListPtr2 == List2.begin())
17730  {
17731  break;
17732  }
17733  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
17734  {
17735  ListPtr2++; //point to one past the cdt
17736  break;
17737  }
17738  }
17739  //ListPtr1 & 2 now at search start position
17740  LP1 = ListPtr1;
17741  LP2 = ListPtr2;
17742  //now search forwards, i.e. for common locations before Location
17743  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
17744  {
17745  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
17746  {
17747  break;
17748  }
17749  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
17750  {
17751  break;
17752  }
17753  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
17754  {
17755  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
17756  {
17757  break;
17758  }
17759  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
17760  {
17761  break;
17762  }
17763  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
17764  {
17765  Utilities->CallLogPop(2230);
17766  return( true);
17767  }
17768  }
17769  }
17770  }
17771 
17772  //now, for the departure analysis, reset the start positions and search locations after Location
17773 
17774  else
17775  {
17776  LP1 = LocPtr1;
17777  LP1++; //start at one past the location itself
17778  LP2 = LocPtr2;
17779  LP2++;
17780  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
17781  {
17782  if(ListPtr1 == List1.end()) //reached end point so stop
17783  {
17784  break;
17785  }
17786  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
17787  {
17788  break;
17789  }
17790  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
17791  {
17792  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
17793  {
17794  break;
17795  }
17796  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
17797  {
17798  break;
17799  }
17800  if((*ListPtr1) == (*ListPtr2)) //found a common later location
17801  {
17802  Utilities->CallLogPop(2231);
17803  return( true);
17804  }
17805  }
17806  }
17807  }
17808  Utilities->CallLogPop(2232);
17809  return( false);
17810 }
17811 
17812 // ---------------------------------------------------------------------------
17813 
17814 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TExitList &ExitList, AnsiString &AllowedExits) const
17815 {
17816  // changed at v2.7.0 to show allowable exit elements
17817  if(ExitList.empty())
17818  {
17819  return("");
17820  }
17821  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
17822  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
17823  AnsiString ExitLocList = "";
17824  AllowedExits = "";
17825 
17826  unsigned int Counter = 0;
17827  for(TExitListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
17828  {
17829  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
17830  Counter++;
17831  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
17832  {
17833  ExitLocList += "\n";
17834  }
17835  }
17836  if(StartName == "")
17837  {
17838  if(ExitList.size() == 1)
17839  {
17840  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
17841  Utilities->CallLogPop(1571);
17842  return(" at " + ID);
17843  }
17844  else
17845  {
17846  Utilities->CallLogPop(1572);
17847  if(ExitList.size() < 4)
17848  {
17849  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17850  return("");
17851  }
17852  else
17853  {
17854  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17855  return("");
17856  }
17857  }
17858  }
17859  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
17860  {
17861  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
17862  {
17863  Utilities->CallLogPop(1570);
17864  if(ExitList.size() < 4)
17865  {
17866  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17867  return("");
17868  }
17869  else
17870  {
17871  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17872  return("");
17873  }
17874  }
17875  }
17876  Utilities->CallLogPop(1569);
17877  if(ExitList.size() < 4)
17878  {
17879  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17880  return(" at " + StartName);
17881  }
17882  else
17883  {
17884  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17885  return(" at " + StartName);
17886  }
17887 }
17888 
17889 // ---------------------------------------------------------------------------
17890 /* can't trust this as locations within a vector may not be contiguous
17891  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
17892  {
17893  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
17894  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
17895  //must be preceded by a TimeLoc departure
17896  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
17897  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
17898  {
17899  if((AVPtr + x) < TDEPtr->ActionVector.end())
17900  {
17901  AnsiString xx = (AVPtr + x)->Command;//test
17902  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
17903  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
17904  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
17905  {
17906  Utilities->CallLogPop();
17907  return false;
17908  }
17909  else if((AVPtr + x)->SequenceType == Finish)
17910  {
17911  Utilities->CallLogPop();
17912  return true;
17913  }
17914  }
17915  }
17916  Utilities->CallLogPop();
17917  return false;
17918  }
17919 */
17920 // ---------------------------------------------------------------------------
17921 
17922 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
17923 {
17924  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
17925  AnsiString FormatStr = "####0.0";
17926  AnsiString AvLateArrMins = "";
17927  AnsiString AvEarlyArrMins = "";
17928  AnsiString AvLatePassMins = "";
17929  AnsiString AvEarlyPassMins = "";
17930  AnsiString AvLateDepMins = "";
17931  AnsiString AvLateExitMins = "";
17932  AnsiString AvEarlyExitMins = "";
17933 
17934  if(LateArrivals > 0)
17935  {
17936  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
17937  }
17938  if(EarlyArrivals > 0)
17939  {
17940  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
17941  }
17942  if(LatePasses > 0)
17943  {
17944  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
17945  }
17946  if(EarlyPasses > 0)
17947  {
17948  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
17949  }
17950  if(LateDeps > 0)
17951  {
17952  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
17953  }
17954  if(LateExits > 0) //added at v2.9.1
17955  {
17956  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
17957  }
17958  if(EarlyExits > 0) //added at v2.9.1
17959  {
17960  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
17961  }
17962  PerfFile << '\n' << '\n' << "***************************************";
17963  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
17964 
17965  if(OnTimeArrivals != 1)
17966  {
17967  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
17968  }
17969  else
17970  {
17971  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
17972  }
17973  if(LateArrivals > 1)
17974  {
17975  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
17976  }
17977  else if(LateArrivals == 1)
17978  {
17979  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
17980  }
17981  else
17982  {
17983  PerfFile << LateArrivals << " late arrivals" << '\n';
17984  }
17985  if(EarlyArrivals > 1)
17986  {
17987  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
17988  }
17989  else if(EarlyArrivals == 1)
17990  {
17991  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
17992  }
17993  else
17994  {
17995  PerfFile << EarlyArrivals << " early arrivals" << '\n';
17996  }
17997  if(OnTimePasses != 1)
17998  {
17999  PerfFile << OnTimePasses << " on-time passes" << '\n';
18000  }
18001  else
18002  {
18003  PerfFile << OnTimePasses << " on-time pass" << '\n';
18004  }
18005  if(LatePasses > 1)
18006  {
18007  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
18008  }
18009  else if(LatePasses == 1)
18010  {
18011  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
18012  }
18013  else
18014  {
18015  PerfFile << LatePasses << " late passes" << '\n';
18016  }
18017  if(EarlyPasses > 1)
18018  {
18019  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
18020  }
18021  else if(EarlyPasses == 1)
18022  {
18023  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
18024  }
18025  else
18026  {
18027  PerfFile << EarlyPasses << " early passes" << '\n';
18028  }
18029 
18030  if(OnTimeExits != 1) //this batch added at v2.9.1
18031  {
18032  PerfFile << OnTimeExits << " on-time exits" << '\n';
18033  }
18034  else
18035  {
18036  PerfFile << OnTimeExits << " on-time exit" << '\n';
18037  }
18038  if(LateExits > 1)
18039  {
18040  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
18041  }
18042  else if(LateExits == 1)
18043  {
18044  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
18045  }
18046  else
18047  {
18048  PerfFile << LateExits << " late exits" << '\n';
18049  }
18050  if(EarlyExits > 1)
18051  {
18052  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
18053  }
18054  else if(EarlyExits == 1)
18055  {
18056  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
18057  }
18058  else
18059  {
18060  PerfFile << EarlyExits << " early exits" << '\n';
18061  }
18062 
18063  if(OnTimeDeps != 1)
18064  {
18065  PerfFile << OnTimeDeps << " on-time departures" << '\n';
18066  }
18067  else
18068  {
18069  PerfFile << OnTimeDeps << " on-time departure" << '\n';
18070  }
18071  if(LateDeps > 1)
18072  {
18073  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
18074  }
18075  else if(LateDeps == 1)
18076  {
18077  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
18078  }
18079  else
18080  {
18081  PerfFile << LateDeps << " late departures" << '\n';
18082  }
18083  TDateTime TempExcessLCDownTime;
18084  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
18085  {
18086 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
18087  //later perf summaries with lower values, changed at v2.8.0
18088 // {
18089  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
18090 // }
18091 /*
18092  else
18093  {
18094  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
18095  }
18096 */
18097  if(TempExcessLCDownTime > TDateTime(0))
18098  {
18099  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
18100  }
18101  }
18102 
18103  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
18104 
18105  if(ExcessLCDownMins > 0.1)
18106  {
18107  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
18108  }
18109  if(MissedStops != 1)
18110  {
18111  PerfFile << MissedStops << " missed stops" << '\n';
18112  }
18113  else
18114  {
18115  PerfFile << MissedStops << " missed stop" << '\n';
18116  }
18117  if(OtherMissedEvents != 1)
18118  {
18119  PerfFile << OtherMissedEvents << " other missed events" << '\n';
18120  }
18121  else
18122  {
18123  PerfFile << OtherMissedEvents << " other missed event" << '\n';
18124  }
18125  if(UnexpectedExits != 1)
18126  {
18127  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
18128  }
18129  else
18130  {
18131  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
18132  }
18133  if(IncorrectExits != 1)
18134  {
18135  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
18136  }
18137  else
18138  {
18139  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
18140  }
18141  if(NumFailures != 1)
18142  {
18143  PerfFile << NumFailures << " train failures" << '\n';
18144  }
18145  else
18146  {
18147  PerfFile << NumFailures << " train failure" << '\n';
18148  }
18149  if(AvHoursIntValue > 0)
18150  {
18151  if(AvHoursIntValue == 1)
18152  {
18153  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
18154  }
18155  else
18156  {
18157  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
18158  }
18159  }
18160  AnsiString AvLateMinsLocsNotReached = "";
18161 
18163  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
18164  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
18165 
18166  if(LocsNotReached > 0)
18167  {
18168  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
18169  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
18170  }
18171  if(SPADRisks != 1)
18172  {
18173  PerfFile << SPADRisks << " SPAD risks" << '\n';
18174  }
18175  else
18176  {
18177  PerfFile << SPADRisks << " SPAD risk" << '\n';
18178  }
18179  if(SPADEvents != 1)
18180  {
18181  PerfFile << SPADEvents << " SPADs" << '\n';
18182  }
18183  else
18184  {
18185  PerfFile << SPADEvents << " SPAD" << '\n';
18186  }
18187  if(Derailments != 1)
18188  {
18189  PerfFile << Derailments << " derailments" << '\n';
18190  }
18191  else
18192  {
18193  PerfFile << Derailments << " derailment" << '\n';
18194  }
18195  if(CrashedTrains != 1)
18196  {
18197  PerfFile << CrashedTrains << " crashed trains" << '\n';
18198  }
18199  else
18200  {
18201  PerfFile << CrashedTrains << " crashed train" << '\n';
18202  }
18203  PerfFile << '\n' << "***************************************" << '\n';
18204 
18205  bool DerailSPADFlag = false, CrashFlag = false;
18206 
18207  int OverallScorePercent = 100;
18208  int TotArrDepExit = 0;
18209  double TotLateMinsFactor = 1;
18210  double MissedStopAndSPADRiskFactor = 1;
18211  double NetNegFactor = 1;
18212 
18214  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
18215  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
18216  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
18217  // 'no timetabled departures... message, which was inappropriate
18218 
18219  if((SPADEvents > 0) || (Derailments > 0))
18220  {
18221  OverallScorePercent = 5; // overrides other calculations
18222  DerailSPADFlag = true;
18223  }
18224  if(CrashedTrains > 0)
18225  {
18226  OverallScorePercent = 0; // overrides other calculations
18227  CrashFlag = true;
18228  }
18229  if(OverallScorePercent == 100)
18230  {
18231  if(TotArrDepExit > 0)
18232  {
18233  TotLateMinsFactor =
18235  ((OtherMissedEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
18236  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
18237  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
18238  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
18239  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
18240  // of arrivals & departures, where 4% = half, 8% = a quarter etc
18241  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
18242  // NetNegfactor: product of the above two
18243  OverallScorePercent = 100 * NetNegFactor;
18244  }
18245  }
18246  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
18247  // flag condits added at v1.1.4 - see above for what the error was
18248  {
18249  AnsiString OneFailureString = ", though the failure would account for some poor performance";
18250  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
18251  AnsiString AddedString = "";
18252  if(NumFailures == 1)
18253  {
18254  AddedString = OneFailureString;
18255  }
18256  if(NumFailures > 1)
18257  {
18258  AddedString = TwoOrMoreFailureString;
18259  }
18260  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
18261  AnsiString Rating = "";
18262  if(OverallScorePercent == 100)
18263  {
18264  Rating = "Perfect!";
18265  }
18266  else if(OverallScorePercent >= 95)
18267  {
18268  Rating = "Excellent";
18269  }
18270  else if(OverallScorePercent >= 90)
18271  {
18272  Rating = "Very good";
18273  }
18274  else if(OverallScorePercent >= 80)
18275  {
18276  Rating = "Good";
18277  }
18278  else if(OverallScorePercent >= 70)
18279  {
18280  Rating = "Fair";
18281  }
18282  else if(OverallScorePercent >= 60)
18283  {
18284  Rating = "Unacceptable" + AddedString;
18285  }
18286  else if(OverallScorePercent >= 50)
18287  {
18288  Rating = "Poor" + AddedString;
18289  }
18290  else if(OverallScorePercent >= 40)
18291  {
18292  Rating = "Bad" + AddedString;
18293  }
18294  else if(OverallScorePercent >= 30)
18295  {
18296  Rating = "Very bad" + AddedString;
18297  }
18298  else if(OverallScorePercent >= 20)
18299  {
18300  Rating = "Terrible" + AddedString;
18301  }
18302  else if(OverallScorePercent >= 10)
18303  {
18304  Rating = "Appalling" + AddedString;
18305  }
18306  else if(OverallScorePercent >= 5)
18307  {
18308  if(DerailSPADFlag)
18309  {
18310  Rating = "Disastrous - potential loss of life";
18311  }
18312  // SPADs/Derailments
18313  else
18314  {
18315  Rating = "Dire" + AddedString;
18316  }
18317  }
18318  else if(OverallScorePercent < 5)
18319  {
18320  if(CrashFlag)
18321  {
18322  Rating = "Catastrophic - loss of life"; // Crashes
18323  }
18324  else
18325  {
18326  Rating = "Abysmal";
18327  }
18328  }
18329  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
18330  }
18331  else
18332  {
18333  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
18334  }
18335  PerfFile << '\n' << "***************************************";
18336  Utilities->CallLogPop(1736);
18337 }
18338 
18339 // ---------------------------------------------------------------------------
18340 
18342 {
18343  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
18344  for(unsigned int x = 0; x < TrainVector.size(); x++)
18345  {
18346  TTrain &Train = TrainVectorAt(58, x);
18347  if(Train.Crashed)
18348  // can't use background colours for crashed & derailed because same colour
18349  {
18350  CrashWarning = true;
18351  }
18352  else if(Train.Derailed)
18353  // can't use background colours for crashed & derailed because same colour
18354  {
18355  DerailWarning = true;
18356  }
18357  else if(Train.BackgroundColour == clSPADBackground)
18358  // use colour as that changes as soon as passes signal
18359  {
18360  SPADWarning = true;
18361  }
18362  else if(Train.BackgroundColour == clTrainFailedBackground)
18363  {
18364  TrainFailedWarning = true;
18365  }
18366  else if(Train.BackgroundColour == clCallOnBackground)
18367  // use colour as also stopped at signal
18368  {
18369  CallOnWarning = true;
18370  }
18371  else if(Train.BackgroundColour == clSignalStopBackground)
18372  // use colour to distinguish from call-on
18373  {
18374  SignalStopWarning = true;
18375  }
18376  else if(Train.BackgroundColour == clBufferAttentionNeeded)
18377  // use colour to distinguish from ordinary buffer stop
18378  {
18379  BufferAttentionWarning = true;
18380  }
18381  }
18382  Utilities->CallLogPop(1796);
18383 }
18384 
18385 // ---------------------------------------------------------------------------
18386 
18388 {
18389  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
18390 
18391  // calculate lateness for running trains
18394  for(unsigned int x = 0; x < TrainVector.size(); x++)
18395  {
18396  TTrain &Train = TrainVectorAt(64, x);
18397  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
18398  AVEntryPtr++)
18399  {
18400  if(AVEntryPtr < Train.ActionVectorEntryPtr)
18401  {
18402  continue;
18403  }
18404  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.StoppedAtLocation && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
18405  TTClockTime))
18406  {
18407  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
18409  }
18410 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
18411  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
18412  TTClockTime))
18413  {
18414  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
18415  OperatingTrainArrDep++;
18416  }
18417 */
18418  }
18419  }
18420 
18421  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
18424 
18425  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
18426  {
18427  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
18428  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
18429  int IncrementalMinutes = 0;
18430  if(AVEntryLast.FormatType == Repeat)
18431  {
18432  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
18433  }
18434  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
18435  {
18436  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
18437  if(TTOD.RunningEntry != NotStarted)
18438  {
18439  continue;
18440  }
18441  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
18442  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
18443  bool TrainOperatingFlag = false;
18444  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
18445  {
18446  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
18447  {
18448  TrainOperatingFlag = true;
18449  break;
18450  }
18451  }
18452  if(TrainOperatingFlag)
18453  {
18454  continue;
18455  }
18456  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
18457  {
18458  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
18459  }
18460  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
18461  {
18462  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
18463  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
18464  {
18465  break; // all the rest will also be greater (& default of -1 will be less)
18466  }
18467  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
18468  {
18469  break; // all the rest will also be greater (& default of -1 will be less)
18470  }
18471  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
18472  {
18473  break; // all the rest will also be greater (& default of -1 will be less)
18474  }
18475  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
18476  {
18477  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
18479  }
18480 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
18481  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
18482  {
18483  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
18484  NotStartedTrainArrDep++;
18485  }
18486 */
18487  }
18488  }
18489  }
18490  Utilities->CallLogPop(1894);
18491 }
18492 
18493 // ---------------------------------------------------------------------------
18494 
18496 // new v2.2.0 for OperatorActionPanel
18497 // clears entries then adds values for running trains then for continuation entries
18498 // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
18499 {
18500  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
18501  OpTimeToActMultiMap.clear();
18502  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
18503 
18504  if(!TrainVector.empty())
18505  // build OpTimeToActMultiMap entries for running trains
18506  {
18507  AnsiString HeadCode;
18508  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
18509  int TrainID;
18510  THCandTrainPosParam HCandTrainPosParam;
18511  for(unsigned int x = 0; x < TrainVector.size(); x++)
18512  {
18513  HeadCode = TrainVectorAt(62, x).HeadCode;
18514  TrainID = TrainVectorAt(63, x).TrainID;
18515  HCandTrainPosParam.first = HeadCode;
18516  HCandTrainPosParam.second = TrainID;
18517  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
18518  if((TimeToAct >= 0) && (TimeToAct < 59.9))
18519  // -1 indicates don't display
18520  {
18521  OpTimeToActMultiMapEntry.first = TimeToAct;
18522  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
18523  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
18524  }
18525  }
18526  }
18527 /*
18528  * class TContinuationTrainExpectationEntry
18529  {
18530  public:
18531  AnsiString Description; ///< service description
18532  AnsiString HeadCode; ///< service headcode
18533  int RepeatNumber; ///< service RepeatNumber
18534  int IncrementalMinutes; ///< Repeat separation in minutes
18535  int IncrementalDigits; ///< Repeat headcode separation
18536  int VectorPosition; ///< TrackVectorPosition for the continuation element
18537  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
18538  };
18539 
18540  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
18541  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
18542  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
18543  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
18544 */
18545 
18547  // build OpTimeToActMultiMap entries for expected trains
18548  {
18549  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
18550  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
18551  float TimeToAct = 0; // minutes
18552  int DistanceToRedSignal = 0; // metres
18553  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
18554  // used to ensure only one train displayed for a given continuation
18555  ContinuationEntryVecPosVector.clear();
18556  bool LaterTrain = false;
18559  {
18560  LaterTrain = false;
18561  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
18562  {
18563  CTEIt++;
18564  continue; // not interested in running or exited trains
18565  }
18566  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
18567  {
18568  CTEIt++;
18569  continue;
18570  // don't include trains not entered yet when a train is already on the continuation
18571  }
18572  if(!ContinuationEntryVecPosVector.empty())
18573  {
18574  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
18575  {
18576  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
18577  {
18578  LaterTrain = true;
18579  ;
18580  // skip past remaining trains waiting to enter at same point
18581  break;
18582  }
18583  }
18584  }
18585  if(LaterTrain)
18586  {
18587  CTEIt++;
18588  continue;
18589  }
18590  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
18591  AnsiString HeadCode = CTEIt->second.HeadCode;
18592  float CurrentStopTime; // set to 0 at start of function
18593  float LaterStopTime; // set to 0 at start of function
18594  float RecoverableTime; // set to 0 at start of function
18595  int AvTrackSpeed; // set to 0 at start of function
18596  int TrainID = -1; // not yet allocated for train still to enter
18597  bool SigControlAndCanPassRedSignal = false;
18598  // doesn't apply for a continuation
18599  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
18600  // EntryPos always 0 for entering at a continuation
18601  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(1),
18602  // at(1) to skip past the Start train value
18603  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
18604  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
18605  // for a train it's the one in front of LeadElement
18606  if(AvTrackSpeed < 30)
18607  {
18608  AvTrackSpeed = 30;
18609  }
18610  if(DistanceToRedSignal == -1)
18611  {
18612  TimeToAct = 60.0;
18613  }
18614  else
18615  {
18616  int Speed = AvTrackSpeed;
18617  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
18618  if(AvTrackSpeed > MaxSpeed)
18619  {
18620  Speed = MaxSpeed;
18621  }
18622  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(1).SignallerControl)
18623  // defined in timetable as under signaller control
18624  {
18625  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
18626  LaterStopTime = 0;
18627  }
18628  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
18629  // accel & decel taken into account in
18630  // CalcDistanceToRedSignalandStopTime
18631  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
18632  // don't need CurrentStopTime or RecoverableTime for continuation entries
18633  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
18634  TimeToAct += MinsBefEnter;
18635  }
18636  THCandTrainPosParam HCandTrainPosParam;
18637  HCandTrainPosParam.first = HeadCode;
18638  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
18639  // -1-CTE... because 2nd value covers TrainID if +ve &
18640  // continuation track vector position if -ve, -1 allows for vecpos being 0
18641  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
18642  {
18643  OpTimeToActMultiMapEntry.first = TimeToAct;
18644  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
18645  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
18646  }
18647  CTEIt++;
18648  }
18649  }
18650  Utilities->CallLogPop(2081);
18651 }
18652 
18653 // ---------------------------------------------------------------------------
18654 
18655 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
18656  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
18657  float &RecoverableTime, int &AvTrackSpeed)
18658 // new v2.2.0
18659 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
18660 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
18661 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
18662 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
18663 // aren't used - this means there is no display for the train in question
18664 {
18665  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
18666  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
18667  int DistanceToRedSignal = 0;
18668  int CumTrackSpeed = 0;
18669  // average track speed, in case need to use in time calc
18670  int TrackSpeedCount = 0;
18671 
18672  //below added at v2.6.1
18673  if(TrainID > -1)
18674  {
18675  TTrain &Train = TrainVectorAtIdent(51, TrainID);
18676  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
18677  Train.StationStopCalculated = false;
18678  }
18679  AvTrackSpeed = 0;
18680  int CurrentElement = TrackVectorPosition;
18681  int CurrentEntryPos = TrackVectorPositionEntryPos;
18682  int NextElement;
18683  int NextEntryPos;
18684  int NextExitPos;
18685 
18686  CurrentStopTime = 0;
18687  LaterStopTime = 0;
18688  RecoverableTime = 0;
18689  if(CurrentElement == -1) // end element, no action needed
18690  {
18691  Utilities->CallLogPop(2094);
18692  return(-1);
18693  }
18694  int CurrentExitPos;
18695 
18696  // get ExitPos for first element to be measured
18697  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
18698  {
18699  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
18700  {
18701  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
18702  {
18703  CurrentExitPos = 1;
18704  }
18705  else
18706  {
18707  CurrentExitPos = 3;
18708  }
18709  }
18710  else
18711  {
18712  CurrentExitPos = 0; // trailing point
18713  }
18714  }
18715  else
18716  {
18717  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
18718  }
18719  // get CumTrackSpeed for first measured element
18720 
18721  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
18722  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
18723 
18724  // check if currently stopped at a location, and if so add the remaining dwell time
18725  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
18726  if(TrainID > -1)
18727  // -1 for a continuation and can't be at a location as not yet entered
18728  {
18729  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
18731  // this used to deduct from RecoverableTime when arrive at a location
18732  if(Train.StoppedAtLocation)
18733  {
18734  if(Train.StoppedForTrainInFront)
18735  {
18736  Utilities->CallLogPop(2082);
18737  return(-1); // no action needed
18738  }
18739  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
18740  {
18741  Utilities->CallLogPop(2083);
18742  return(-1); // not due a departure so no action needed
18743  }
18744  else // due a departure
18745  {
18746  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
18747  // can't convert a TDateTime to a float directly
18748  CurrentStopTime = float(TimeToDepart);
18749  AVPtr++;
18750  }
18751  }
18752  }
18753  // check if CurrentElement is a red signal, but ok if autosig route after
18754  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
18755  // ok if autosig route after red signal
18756  {
18757  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
18758  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
18759  int RouteNumber; // holder for referenced value, not used
18760  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
18761  {
18762  Utilities->CallLogPop(2078);
18763  return(-1);
18764  }
18765  else if(SigControlAndCanPassRedSignal)
18766  // ignore signal and increment CurrentElement to NextElement
18767  {
18768  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
18769  {
18770  if((NextEntryPos == 0) || (NextEntryPos == 2))
18771  // leading entry point
18772  {
18773  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
18774  {
18775  NextExitPos = 1;
18776  }
18777  else
18778  {
18779  NextExitPos = 3;
18780  }
18781  }
18782  else
18783  {
18784  NextExitPos = 0; // trailing entry point
18785  }
18786  }
18787  else
18788  {
18789  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
18790  }
18791  CurrentElement = NextElement;
18792  CurrentEntryPos = NextEntryPos;
18793  CurrentExitPos = NextExitPos;
18794  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
18795  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
18796  }
18797  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
18798  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
18799  {
18800  Utilities->CallLogPop(2084);
18801  return(0);
18802  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
18803  }
18804  }
18805  int LaterStopNumber = 0;
18806  int x = 0;
18807  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
18808 
18809  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
18810  // not red signal next (in fwd direction) so enter loop to calc CumLength
18811  {
18812  x++; // added in v2.4.0 as above
18813  if(x > 5000)
18814  {
18815  Utilities->CallLogPop(2120);
18816  return(-1);
18817  }
18818  if(CurrentEntryPos > 1)
18819  {
18820  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
18821  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
18822  }
18823  else
18824  {
18825  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
18826  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
18827  }
18828  TrackSpeedCount++;
18829 
18830  // added at v2.6.1 to find DistanceToStationStop for trains running early
18831  if(TrainID > -1) //can ignore continuation entries as these don't run early
18832  {
18833  TTrain &Train = TrainVectorAtIdent(52, TrainID);
18834  if(!Train.StationStopCalculated)
18835  {
18836  if(Train.TrainMode == Timetable)
18837  {
18838  bool StopRequired = false;
18839  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
18840  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
18841  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
18842  {
18843  // no need to add in the length of element to CumulativeLength
18844  if(StopRequired)
18845  {
18846  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
18847  Train.StationStopCalculated = true; //don't want to update it with later stops
18848  }
18849  }
18850  }
18851  }
18852  }
18853  // check for train in front, but if on a bridge on other track then ok
18854  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
18855  int TrainOnElement;
18856  if(TE.TrackType != Bridge)
18857  {
18858  TrainOnElement = TE.TrainIDOnElement;
18859  }
18860  else
18861  {
18862  if(CurrentEntryPos > 1)
18863  {
18864  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
18865  }
18866  else
18867  {
18868  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
18869  }
18870  }
18871  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
18872  // train in front before red signal
18873  {
18874  Utilities->CallLogPop(2085);
18875  return(-1);
18876  }
18877  // add to stoptime if required
18878  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
18879  {
18880  double StopTimeDouble;
18881  while(AVPtr->FormatType == PassTime)
18882  {
18883  AVPtr++; // skip past any passes
18884  }
18885  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
18886  (AVPtr->FormatType == TimeTimeLoc)))
18887  // stop due here so calc dwell time & advance Ptr
18888  {
18889  if(AVPtr->FormatType == TimeTimeLoc)
18890  {
18891  LaterStopNumber++;
18892  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
18893  if(StopTimeDouble < 0.5)
18894  {
18895  StopTimeDouble = 0.5;
18896  }
18897  // at least 30 secs delay at station
18898  // can't convert a TDateTime to a float directly
18899  LaterStopTime += float(StopTimeDouble);
18900  RecoverableTime += StopTimeDouble - 0.5;
18901  if((LaterStopNumber == 1) && (TrainID > -1))
18902  {
18903  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
18904  }
18905  AVPtr++;
18906  }
18907  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
18908  {
18909  if((AVPtr + 1)->FormatType == TimeLoc)
18910  // must be a departure
18911  {
18912  LaterStopNumber++;
18913  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
18914  // can't convert a TDateTime to a float directly
18915  if(StopTimeDouble < 0.5)
18916  {
18917  StopTimeDouble = 0.5;
18918  }
18919  // at least 30 secs delay at station
18920  LaterStopTime += float(StopTimeDouble);
18921  RecoverableTime += StopTimeDouble - 0.5;
18922  if((LaterStopNumber == 1) && (TrainID > -1))
18923  {
18924  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
18925  }
18926  AVPtr++;
18927  AVPtr++;
18928  }
18929  else // not a departure, does something else at the location so no calculation needed
18930  {
18931  Utilities->CallLogPop(2086);
18932  return(-1);
18933  }
18934  }
18935  }
18936  }
18937  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
18938  if(NextElement == -1) // reached end element, no action needed
18939  {
18940  Utilities->CallLogPop(2077);
18941  return(-1);
18942  }
18943  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
18944  // get NextExitPos
18945  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
18946  {
18947  if((NextEntryPos == 0) || (NextEntryPos == 2))
18948  // leading entry point
18949  {
18950  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
18951  {
18952  NextExitPos = 1;
18953  }
18954  else
18955  {
18956  NextExitPos = 3;
18957  }
18958  }
18959  else
18960  {
18961  NextExitPos = 0; // trailing entry point
18962  }
18963  }
18964  else
18965  {
18966  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
18967  }
18968  CurrentElement = NextElement;
18969  CurrentEntryPos = NextEntryPos;
18970  CurrentExitPos = NextExitPos;
18971  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
18972  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
18973  }
18974  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
18975  // ok if autosig route after red signal, no action needed
18976  {
18977  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
18978  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
18979  int RouteNumber; // holder for referenced value, not used
18980  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
18981  {
18982  Utilities->CallLogPop(2095);
18983  return(-1);
18984  }
18985  }
18986  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
18987  // average line speed/2 (for half distance accelerating and half decelerating.
18988 
18989  float MaxAllowableSpeed;
18990 
18991  if(TrackSpeedCount > 0)
18992  {
18993  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
18994  }
18995  else // shouldn't reach here but include to prevent divide by zero error
18996  {
18997  if(CurrentEntryPos > 1)
18998  {
18999  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
19000  }
19001  else
19002  {
19003  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
19004  }
19005  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
19006  }
19007  float KmPerLocationStop;
19008 
19009  if(LaterStopNumber > 0)
19010  {
19011  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
19012  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
19013  }
19014  else
19015  {
19016  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
19017  // using linear trendline for accel & decel distance at various speeds
19018  // at half braking, speed never < 60 using this
19019  }
19020  if(AvTrackSpeed > MaxAllowableSpeed)
19021  {
19022  AvTrackSpeed = MaxAllowableSpeed;
19023  }
19024  Utilities->CallLogPop(2096);
19025  return(DistanceToRedSignal);
19026 }
19027 
19028 // ---------------------------------------------------------------------------
19029 // end of TTrainController entries
19030 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:8341
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17470
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:113
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:347
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:50
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:14113
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:314
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:313
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:443
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:646
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:62
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1700
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:885
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:151
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:14136
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:13780
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:335
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:18495
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:754
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:15161
TTrain::CheckNewServiceDepartureTime
AnsiString CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time
Definition: TrainUnit.cpp:6971
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:93
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:51
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:981
Create
@ Create
Definition: TrainUnit.h:50
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:733
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:18476
Arrive
@ Arrive
Definition: TrainUnit.h:50
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:50
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:312
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:353
Depart
@ Depart
Definition: TrainUnit.h:50
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:702
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:11007
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:700
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:393
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:792
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:299
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7061
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1617
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:51
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:891
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1629
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:728
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:119
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:776
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1641
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:68
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:64
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:460
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:694
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6129
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:65
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:397
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:69
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:19225
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:13064
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:152
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:335
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:967
TTrain::TrainFailed
bool TrainFailed
added at v2.4.0 to indicate failure
Definition: TrainUnit.h:379
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:950
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:740
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:308
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:946
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7217
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:355
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:434
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:335
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6243
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:740
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:9407
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:335
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:882
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:668
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:14892
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:798
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:973
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:619
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:980
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:184
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:18266
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:740
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:10960
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1621
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:8313
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:113
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:933
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:690
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:14907
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:242
Intermediate
@ Intermediate
Definition: TrainUnit.h:75
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:931
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6058
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:947
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3438
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:628
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:64
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:456
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:418
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:9369
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:52
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:449
NotStarted
@ NotStarted
Definition: TrainUnit.h:86
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10625
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:792
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:736
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:315
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:705
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:343
TTrainDataEntry
Contains all data for a single train.
Definition: TrainUnit.h:184
LeadMid
@ LeadMid
Definition: TrainUnit.h:275
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:783
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:18740
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:13860
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:945
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6264
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:686
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:987
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:229
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:983
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:192
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:802
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:760
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:676
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:8619
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:15010
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:275
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:221
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:839
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:15869
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:14948
StartNew
@ StartNew
Definition: TrainUnit.h:64
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:50
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:275
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:796
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:316
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:51
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:408
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:730
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:18014
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:10568
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:956
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5673
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:463
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:163
TTrain
Definition: TrainUnit.h:279
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:953
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:10587
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:154
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11255
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:834
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:227
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:816
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:440
GapJump
@ GapJump
Definition: TrackUnit.h:67
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:974
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:786
NoSequence
@ NoSequence
Definition: TrainUnit.h:75
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:150
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:150
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:375
Finish
@ Finish
Definition: TrainUnit.h:75
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6009
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:9005
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:335
TTrackElement::TempTrackMarker23
bool TempTrackMarker23
Utility markers for program use.
Definition: TrackUnit.h:140
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1668
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:335
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:329
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:202
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:649
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:333
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:14989
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:779
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:298
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:174
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:731
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:422
TExitListIterator
TExitList::iterator TExitListIterator
Definition: TrainUnit.h:95
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:664
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:14712
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1384
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:5905
End
@ End
Definition: TrackUnit.h:77
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:365
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6845
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8601
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:310
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10354
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:65
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:317
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:371
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:15072
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:126
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:941
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:581
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:991
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:791
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:186
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:780
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:387
SignalPost
@ SignalPost
Definition: TrackUnit.h:67
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:935
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled action, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:426
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:143
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1645
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:318
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:11631
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:963
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:186
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5338
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:127
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:793
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:717
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:843
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:385
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:76
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:436
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:66
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:789
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:754
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:879
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1859
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:440
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:787
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:951
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:64
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:95
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:113
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:440
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:10640
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:929
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:416
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:954
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:9905
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:688
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:335
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:414
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:79
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:979
Pass
@ Pass
Definition: TrainUnit.h:52
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:740
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:702
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:70
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:2962
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11073
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:771
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:69
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:966
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:447
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:720
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:824
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:7433
TExitList
std::list< int > TExitList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:91
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:939
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:662
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:323
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:292
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:972
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:766
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:8325
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:975
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:188
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:64
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:198
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6701
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:757
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:17190
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:428
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:14381
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10491
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:600
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:286
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3170
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:986
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:424
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:4928
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2483
RearSplit
@ RearSplit
Definition: TrainUnit.h:50
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:111
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:52
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:813
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:612
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:936
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:764
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:959
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:136
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10389
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2894
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6673
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:14479
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:52
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:879
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:13164
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:164
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:785
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:808
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:349
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3388
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:932
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:300
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8135
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:7414
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2886
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:770
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:443
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:420
NewService
@ NewService
Definition: TrainUnit.h:50
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:15225
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1484
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:440
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:351
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1628
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:341
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:200
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:196
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:777
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:169
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:976
Crossover
@ Crossover
Definition: TrackUnit.h:67
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4538
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:682
AtLocation
@ AtLocation
Definition: TrainUnit.h:70
Signal
@ Signal
Definition: TrackUnit.h:77
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:938
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:969
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:785
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:154
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17707
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:822
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:17454
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1629
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:422
Exited
@ Exited
Definition: TrainUnit.h:86
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10340
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:14923
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7701
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:17533
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:988
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2100
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:440
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:57
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1650
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:928
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:440
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:700
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:288
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:742
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:50
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:395
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:754
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:958
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:934
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:335
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:64
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:970
Leave
@ Leave
Definition: TrainUnit.h:50
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:795
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:712
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:738
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:412
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:363
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2867
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:937
NoFormat
@ NoFormat
Definition: TrainUnit.h:64
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:750
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:64
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2412
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:440
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3417
Enter
@ Enter
Definition: TrainUnit.h:50
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:514
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7648
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:944
TTrain::NewTrainService
void NewTrainService(int Caller)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6203
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:14972
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:406
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:253
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:13694
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:692
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:8654
Timetable
@ Timetable
Definition: TrainUnit.h:58
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:14067
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6756
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:748
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:10924
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:776
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:109
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:17376
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:740
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:287
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:18387
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:521
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:80
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:401
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:710
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:240
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:746
Terminate
@ Terminate
Definition: TrainUnit.h:50
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:754
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
map of active track element names
Definition: TrackUnit.h:788
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:440
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:148
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:454
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:338
Running
@ Running
Definition: TrainUnit.h:86
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:262
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3514
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:880
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13036
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:14491
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed)
new v2.2.0, calcs distance to red signal, returns -1 for no signal found, for autosigs route after ne...
Definition: TrainUnit.cpp:18655
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:15248
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:414
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11144
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:726
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:215
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:796
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:389
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2297
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:18391
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:812
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1615
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:9091
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:8706
TTrainController::ControllerCheckNewServiceDepartureTime
AnsiString ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::CheckNewServiceDepartureTime for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:9598
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:806
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:961
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:129
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:516
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:8717
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:978
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1702
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:754
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3366
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3066
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:422
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:796
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:11048
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:70
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:52
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1613
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:8282
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:2930
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:101
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:773
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:15094
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:243
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:955
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:14458
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:146
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:814
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:441
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4494
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:255
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TExitList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:17814
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:9423
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:51
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:325
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:373
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:11131
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:769
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:98
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:8731
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:154
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:887
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:404
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:731
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:159
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1683
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:190
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:131
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:960
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:204
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:148
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:762
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:895
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:534
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:4951
TActionVectorEntry::ExitList
TExitList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:115
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:424
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1619
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1629
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:117
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:304
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:984
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:655
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6503
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:653
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:9075
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:878
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:778
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:321
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:15261
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:763
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:949
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:152
Points
@ Points
Definition: TrackUnit.h:67
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:724
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:361
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:772
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:13593
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6544
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:359
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:754
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:794
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:52
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3362
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:968
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:367
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:458
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:16637
Continuation
@ Continuation
Definition: TrackUnit.h:67
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:740
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7406
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:761
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:788
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:432
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:9777
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:331
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:17922
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3024
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:883
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:52
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:723
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:886
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:982
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:9454
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:89
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:438
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:622
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:740
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6446
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:790
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:445
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:281
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:179
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:465
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:52
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:965
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:19055
TTrackElement::TempTrackMarker01
bool TempTrackMarker01
Definition: TrackUnit.h:140
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:889
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:957
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:10782
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:290
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:261
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:720
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:664
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8245
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:63
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:740
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:295
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:709
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:164
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:707
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:952
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:18164
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:18120
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:744
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:103
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:259
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:75
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:890
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:302
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:49
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:664
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:335
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:782
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:989
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:80
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:9045
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:14405
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7020
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:17390
TTrain::AValue
double AValue
this is a useful shorthand value in calculating speeds and transit times in SetTrainMovementValues [=...
Definition: TrainUnit.h:381
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5390
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:17212
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:9060
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:125
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13043
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:311
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:8389
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:15054
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:162
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:154
NoLocation
@ NoLocation
Definition: TrainUnit.h:70
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:900
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:357
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:194
NoMode
@ NoMode
Definition: TrainUnit.h:58
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:784
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:11479
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:101
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:713
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:754
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:64
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:153
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:213
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:804
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:776
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:752
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:531
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5035
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:440
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:9442
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:948
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:142
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:274
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:44
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:725
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable (the object is a member of TTrainController)
Definition: TrainUnit.h:217
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:14101
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7145
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:410
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:651
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1623
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:121
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:327
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:715
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:942
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:985
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:711
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:2988
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled action entries, null on creation
Definition: TrainUnit.h:103
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:123
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:377
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:977
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:103
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4661
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:684
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6606
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:150
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:369
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:800
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:754
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:940
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6169
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:709
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:64
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:51
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:714
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:18341
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:680
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:64
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:768
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:971
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:818
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:666
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:105
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:4940
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:399
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:150
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:670
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:306
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:751
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:713
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:65
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:781
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2912
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:91
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:626
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1038
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:962
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:17496
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6474
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3408
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:107
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2500
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:189
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:186
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6787
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:309
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:77
Repeat
@ Repeat
Definition: TrainUnit.h:65
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:930
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:13052
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:688
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:754
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:159
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:68
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:149
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:441
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:964
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:745
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:829
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:225
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5700
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:17238
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:144
Signaller
@ Signaller
Definition: TrainUnit.h:58
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:451
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:888
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:160
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:876
Bridge
@ Bridge
Definition: TrackUnit.h:67
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:943
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:257
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:335
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:74
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:391
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:75
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:524
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:67
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:111
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:103